Bagaimana cara menambahkan fungsionalitas ke objek yang sudah ada?

25

Saya memiliki antarmuka yang memiliki sejumlah fungsionalitas yang terdefinisi dengan baik. Katakanlah:

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

Ini berfungsi dengan baik untuk sebagian besar implementasi antarmuka, tetapi dalam beberapa kasus, saya perlu menambahkan beberapa fungsi baru (seperti mungkin digulirkan ke metode baru, createBrownies()). Pendekatan yang jelas / naif untuk melakukan ini adalah memperluas antarmuka:

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

Tetapi memiliki kelemahan yang cukup besar karena saya tidak dapat menambahkan fungsionalitas baru tanpa memodifikasi API yang ada (seperti mengubah kelas untuk menggunakan antarmuka baru).

Saya sedang berpikir tentang menggunakan adaptor untuk menambah fungsionalitas setelah instantiation:

class BrownieAdapter {
  private brownieBakery;

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

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

Yang akan memberi saya sesuatu seperti:

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

Ini sepertinya solusi yang bagus untuk masalah ini, tetapi saya bertanya-tanya apakah saya membangunkan dewa-dewa lama dengan melakukannya. Apakah adaptor bisa digunakan? Apakah ada pola yang lebih baik untuk diikuti? Atau haruskah saya benar-benar menggigit peluru dan hanya memperluas antarmuka asli?


sumber
Delphi memiliki kelas pembantu, itu seperti menambahkan metode ke kelas yang ada tanpa benar-benar memodifikasi mereka. Misalnya Delphi memiliki kelas TBitmap yang didefinisikan dalam unit grafiknya, Anda dapat membuat kelas pembantu yang menambahkan, katakanlah, fungsi Balik ke TBitmap. Selama kelas helper berada dalam ruang lingkup Anda dapat memanggil MyBitmap.Flip;
Bill

Jawaban:

14

Salah satu Handle pola Tubuh bisa cocok dengan deskripsi, tergantung pada persyaratan yang tepat Anda, bahasa, dan tingkat yang diperlukan abstraksi.

Pendekatan murni adalah pola dekorator , yang melakukan persis apa yang Anda cari, secara dinamis menambah tanggung jawab pada objek. Jika Anda benar-benar membangun toko roti, itu pasti berlebihan dan Anda hanya harus menggunakan Adaptor.

yannis
sumber
Inilah yang saya butuhkan: Saya menyadari menggunakan adaptor akan mengacaukan injeksi ketergantungan, tetapi menggunakan dekorator menyiasatinya.
5

Selidiki konsep penggunaan kembali horisontal , di mana Anda dapat menemukan hal-hal seperti Ciri , Pemrograman Berorientasi Aspek yang masih eksperimental namun sudah produksi dan Mixin yang terkadang dibenci .

Cara langsung menambahkan metode ke kelas juga tergantung pada bahasa pemrograman. Ruby memungkinkan untuk menambal-monyet sementara warisan Javascript's prototipe , di mana kelas tidak benar-benar ada, Anda membuat objek dan hanya menyalinnya dan terus menambahkannya, misalnya:

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();

Akhirnya, Anda juga dapat meniru penggunaan kembali horisontal, atau "modifikasi" runtime dan "penambahan" perilaku kelas / objek dengan refleksi .

dukeofgaming
sumber
4

Jika ada persyaratan bahwa bakeryinstance harus mengubah perilakunya secara dinamis (tergantung pada tindakan pengguna, dll.) Maka Anda harus menggunakan pola Penghias .

Jika bakerytidak mengubah perilakunya secara dinamis tetapi Anda tidak dapat memodifikasi Bakery class(API eksternal, dll.) Maka Anda harus menggunakan pola Adaptor .

Jika bakerytidak mengubah perilakunya secara dinamis dan Anda dapat memodifikasi Bakery classmaka Anda harus memperluas antarmuka yang ada (seperti yang Anda usulkan pada awalnya) atau memperkenalkan antarmuka baru BrownieInterface dan biarkan Bakerymenerapkan dua antarmuka BakeryInterfacedan BrownieInterface.
Kalau tidak, Anda akan menambah kompleksitas yang tidak perlu ke kode Anda (menggunakan pola Dekorator) tanpa alasan yang bagus!

Ketip
sumber