ฉันจะเพิ่มฟังก์ชันการทำงานให้กับวัตถุที่มีอยู่แล้วได้อย่างไร


25

ฉันมีอินเทอร์เฟซที่มีฟังก์ชันการทำงานที่กำหนดไว้จำนวนหนึ่ง สมมติว่า:

interface BakeryInterface {
  public function createCookies();
  public function createIceCream();
}

สิ่งนี้ทำงานได้ดีสำหรับการนำไปใช้งานส่วนใหญ่ของอินเทอร์เฟซ แต่ในบางกรณีฉันต้องเพิ่มฟังก์ชันใหม่บางอย่าง (เช่นอาจรีดเป็นวิธีใหม่createBrownies()) วิธีการที่ชัดเจน / ไร้เดียงสาในการทำเช่นนี้คือการขยายส่วนต่อประสาน:

interface BrownieBakeryInterface extends BakeryInterface {
  public function createBrownies();
}

แต่มีข้อเสียใหญ่ที่ฉันไม่สามารถเพิ่มฟังก์ชั่นใหม่โดยไม่ต้องปรับเปลี่ยน API ที่มีอยู่ (เช่นการเปลี่ยนคลาสเพื่อใช้อินเทอร์เฟซใหม่)

ฉันกำลังคิดเกี่ยวกับการใช้อะแดปเตอร์เพื่อเพิ่มฟังก์ชันการทำงานหลังจาก instantiation:

class BrownieAdapter {
  private brownieBakery;

  public function construct(BakeryInterface bakery) {
    this->brownieBakery = bakery;
  }

  public function createBrownies() {
    /* ... */
  }
}

ซึ่งจะทำให้ฉันชอบ:

bakery = new Bakery();
bakery = new BrownieBakery(bakery);
bakery->createBrownies();

ดูเหมือนว่าจะเป็นทางออกที่ดีสำหรับปัญหา แต่ฉันสงสัยว่าถ้าฉันตื่นขึ้นมาพระเจ้าเก่าด้วยการทำมัน อะแดปเตอร์เป็นวิธีที่จะไป? มีรูปแบบที่ดีกว่าในการติดตาม? หรือฉันควรจะกัดกระสุนและแค่ขยายส่วนต่อประสานดั้งเดิม?


Delphi มีคลาสตัวช่วยมันเหมือนกับการเพิ่มเมธอดให้กับคลาสที่มีอยู่ ตัวอย่างเช่น Delphi มีคลาส TBitmap ที่กำหนดไว้ในหน่วยกราฟิกของมันคุณสามารถสร้างคลาสผู้ช่วยที่เพิ่มฟังก์ชั่น Flip เพื่อ TBitmap ตราบใดที่คลาสตัวช่วยอยู่ในขอบเขตคุณสามารถเรียก MyBitmap.Flip;
Bill

คำตอบ:


14

รูปแบบของตัวจัดการใด ๆสามารถตรงกับคำอธิบายขึ้นอยู่กับความต้องการที่แน่นอนภาษาและระดับที่ต้องการ

วิธีการที่พิถีพิถันจะเป็นรูปแบบมัณฑนากรซึ่งทำสิ่งที่คุณต้องการอย่างแท้จริงเพิ่มความรับผิดชอบให้กับวัตถุ หากคุณกำลังสร้างร้านเบเกอรี่จริง ๆ มันเกินความจริงแล้วและคุณควรไปกับอะแดปเตอร์


นี่คือสิ่งที่ฉันต้องการ: ฉันตระหนักว่าการใช้อะแดปเตอร์จะทำให้สกรูด้วยการฉีดขึ้นอยู่กับการพึ่งพา

5

ตรวจสอบแนวคิดของที่นำมาใช้ในแนวนอนซึ่งคุณสามารถค้นหาสิ่งเช่นลักษณะที่ยังคงทดลอง แต่การผลิตหลักฐานแล้วมุมมอง Oriented Programmingและบางครั้งก็เกลียดMixins

วิธีการเพิ่มเมธอดโดยตรงไปยังคลาสนั้นขึ้นอยู่กับภาษาการเขียนโปรแกรมด้วย Ruby อนุญาตให้มีการปะแก้ลิงในขณะที่สืบทอดตามต้นแบบของ Javascript ซึ่งคลาสไม่ได้มีอยู่จริงคุณสร้างวัตถุและเพียงแค่คัดลอกและเพิ่มไปเรื่อย ๆ เช่น:

var MyClass = {
    do : function(){...}
};

var MyNewClass = new MyClass;
MyClass.undo = function(){...};


var my_new_object = new MyNewClass;
my_new_object.do();
my_new_object.undo();

สุดท้ายคุณก็ยังสามารถนำมาใช้ใหม่เลียนแบบแนวนอนหรือรันไทม์ "ปรับเปลี่ยน" และ "นอกจากนี้" พฤติกรรมระดับ / วัตถุที่มีการสะท้อน


4

หากมีความต้องการที่bakeryเช่นต้องเปลี่ยนพฤติกรรมของมันแบบไดนามิก (ขึ้นอยู่กับการกระทำของผู้ใช้ ฯลฯ ) แล้วคุณควรไปสำหรับรูปแบบมัณฑนากร

หากbakeryไม่มีการเปลี่ยนแปลงแบบไดนามิกพฤติกรรมของมัน แต่คุณไม่สามารถปรับเปลี่ยนBakery class(API ภายนอก ฯลฯ ) แล้วคุณควรไปสำหรับรูปแบบอะแดปเตอร์

ถ้าbakeryไม่เปลี่ยนพฤติกรรมแบบไดนามิกและคุณสามารถปรับเปลี่ยนBakery classแล้วคุณควรขยายอินเตอร์เฟซที่มีอยู่ (ตามที่คุณเสนอแรก) หรือแนะนำอินเตอร์เฟซใหม่ BrownieInterfaceและให้Bakeryดำเนินการสองอินเตอร์เฟซและ BakeryInterface มิฉะนั้นคุณจะต้องเพิ่มความซับซ้อนที่ไม่จำเป็นในรหัสของคุณ (โดยใช้รูปแบบมัณฑนากร) โดยไม่มีเหตุผล!BrownieInterface


2

คุณพบปัญหาการแสดงออก บทความนี้โดย Stuart Sierraกล่าวถึงวิธีแก้ปัญหาการปิดกั้น แต่เขายังยกตัวอย่างใน Java และแสดงวิธีแก้ปัญหายอดนิยมใน OO แบบคลาสสิก

นอกจากนี้ยังมีการนำเสนอโดย Chris Houserซึ่งพูดถึงเรื่องเดียวกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.