Bisakah kita sepenuhnya mengganti warisan menggunakan pola strategi dan injeksi ketergantungan?


10

Sebagai contoh:

var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)

Karena kelas Bebek berisi semua perilaku (abstrak), membuat kelas baru MallardDuck(yang meluas Duck) tampaknya tidak diperlukan.

Referensi: Pola Desain Kepala Pertama, Bab 1.


Apa jenis Duckbehavior.quackBehaviordan bidang lain dalam kode Anda?
maks 630

Tidak ada injeksi ketergantungan pada contoh Anda.
David Conrad

11
Warisan luar biasa ketika Anda seorang pengembang tingkat junior hingga menengah karena ada sejarah panjang trik dan pola desain refactoring di sekitarnya. Tetapi sebagian besar pengembang berpengalaman yang saya ajak bicara lebih suka hierarki warisan yang dangkal berdasarkan komposisi bila memungkinkan. Warisan dapat menyebabkan kopling lebih kencang dari yang diperlukan dan dapat membuat perubahan sulit.
Mark Rogers


5
@ Davidvidon: OP tidak melakukan pendataan baru di dalam kelas . DI / IoC adalah tentang menyuntikkan dependensi, bukan tentang wadah atau tentang tidak pernah menggunakan "baru"; itu perhatian sepenuhnya ortogonal. Selain itu, Anda selalu harus mengubah kode di suatu tempat - baik itu komposisi root atau file konfigurasi. Kontrol terbalik di sini dalam tipe Bebek, karena hal yang mengontrol penciptaan dependensi bukanlah si Bebek, tetapi beberapa konteks luar; ini menjadi contoh mainan yang diberikan untuk kejelasan, sangat baik bahwa konteks luar hanya diwakili oleh kode panggilan.
Filip Milovanović

Jawaban:


21

Tentu, tetapi kami menyebutnya komposisi dan delegasi . Pola Strategi dan Injeksi Ketergantungan mungkin tampak serupa secara struktural tetapi niat mereka berbeda.

Pola Strategi memungkinkan modifikasi perilaku runtime di bawah antarmuka yang sama. Saya bisa memberi tahu bebek mallard untuk terbang dan melihatnya terbang dengan sayap. Kemudian tukarkan dengan bebek pilot jet dan saksikan ia terbang dengan maskapai Delta. Melakukan itu ketika program sedang berjalan adalah hal Pola Strategi.

Dependency Injection adalah teknik untuk menghindari ketergantungan pengkodean keras sehingga mereka dapat berubah secara mandiri tanpa mengharuskan klien untuk dimodifikasi ketika mereka berubah. Klien hanya mengungkapkan kebutuhan mereka tanpa mengetahui bagaimana mereka akan dipenuhi. Jadi bagaimana mereka bertemu diputuskan di tempat lain (biasanya di utama). Anda tidak perlu dua bebek untuk menggunakan teknik ini. Hanya sesuatu yang menggunakan bebek tanpa mengetahui atau peduli bebek mana. Sesuatu yang tidak membangun bebek atau mencarinya tetapi sangat senang menggunakan bebek apa pun yang Anda berikan.

Jika saya memiliki kelas bebek beton saya bisa menerapkannya perilaku terbang. Saya bahkan dapat mengubah perilaku dari terbang-dengan-sayap ke terbang-dengan-Delta berdasarkan variabel negara. Variabel itu bisa berupa boolean, int, atau bisa juga FlyBehavioryang memiliki flymetode yang melakukan gaya terbang apa pun tanpa saya harus mengujinya dengan if. Sekarang saya bisa mengubah gaya terbang tanpa mengubah jenis bebek. Sekarang Mallard bisa menjadi pilot. Ini adalah komposisi dan delegasi . Bebek ini terdiri dari FlyBehavior dan dapat mendelegasikan permintaan terbang ke sana. Anda dapat mengganti semua perilaku bebek sekaligus atau menahan sesuatu untuk setiap perilaku atau kombinasi di antaranya.

Ini memberi Anda semua kekuatan yang sama yang dimiliki warisan kecuali satu. Inheritance memungkinkan Anda mengekspresikan metode Duck apa yang Anda timpa dalam subtipe Bebek. Komposisi dan delegasi mengharuskan Bebek untuk secara eksplisit mendelegasikan subtipe dari awal. Ini jauh lebih fleksibel tetapi melibatkan lebih banyak mengetik keyboard dan Duck harus tahu itu terjadi.

Namun, banyak orang percaya bahwa warisan harus dirancang secara eksplisit sejak awal. Dan jika belum, Anda harus menandai kelas Anda sebagai disegel / final untuk melarang pewarisan. Jika Anda mengambil pandangan itu maka warisan benar-benar tidak memiliki keunggulan dibandingkan komposisi dan delegasi. Karena dengan cara apa pun Anda harus merancang ekstensibilitas dari awal atau bersedia meruntuhkannya nanti.

Meruntuhkan segalanya sebenarnya adalah pilihan populer. Perlu diketahui bahwa ada kasus di mana itu menjadi masalah. Jika Anda secara mandiri menggunakan pustaka atau modul kode yang tidak ingin Anda perbarui dengan rilis berikutnya, Anda bisa terjebak berurusan dengan versi kelas yang tidak tahu apa-apa tentang apa yang sedang Anda lakukan sekarang.

Meskipun Anda bersedia meruntuhkannya nanti dapat membebaskan Anda dari mendesain berlebihan, ada sesuatu yang sangat kuat untuk bisa mendesain sesuatu yang menggunakan bebek tanpa harus tahu apa yang sebenarnya akan dilakukan bebek ketika digunakan. Tidak mengetahui itu adalah hal yang kuat. Ini memungkinkan Anda berhenti memikirkan bebek untuk sementara waktu dan memikirkan sisa kode Anda.

"Bisakah kita" dan "haruskah kita" adalah pertanyaan yang berbeda. Nikmat Komposisi daripada Warisan tidak mengatakan tidak pernah menggunakan warisan. Masih ada kasus-kasus di mana warisan paling masuk akal. Saya akan menunjukkan kepada Anda contoh favorit saya :

public class LoginFailure : System.ApplicationException {}

Warisan memungkinkan Anda membuat pengecualian dengan nama deskriptif yang lebih spesifik hanya dalam satu baris.

Coba lakukan itu dengan komposisi dan Anda akan mendapatkan kekacauan. Juga, tidak ada risiko masalah warisan yo-yo karena tidak ada data atau metode di sini untuk menggunakan kembali dan mendorong rantai pewarisan. Semua ini menambahkan adalah nama yang bagus. Jangan pernah meremehkan nilai nama baik.


1
" Jangan pernah meremehkan nilai nama baik ". Waktu pakaian baru Kaisar: LoginExceptionbukan nama yang baik. Ini klasik "penamaan smurf" dan, jika saya mengambil Exception, semua yang saya miliki adalah Login, itu tidak memberi tahu saya apa yang salah.
David Arno

Sayangnya kita terjebak dengan konvensi konyol menempatkan Exceptiondi akhir setiap kelas pengecualian, tapi tolong jangan bingung "terjebak dengan itu" dengan "baik".
David Arno

@ Davidvido Jika Anda dapat menyarankan nama yang lebih baik saya semua telinga. Di sini kita mendapat manfaat dari tidak terjebak dalam konvensi basis kode yang ada. Jika Anda memiliki kekuatan untuk mengubah dunia dengan menjentikkan jari Anda, apa yang akan Anda beri nama?
candied_orange

Aku akan nama mereka, StackOverflow, OutOfMemory, NullReferenceAccess, LoginFailuredll Pada dasarnya, mengambil "Exception" off nama. Dan, jika diperlukan, perbaiki mereka sehingga menggambarkan apa yang salah.
David Arno

@ DavidArno dengan perintah Anda.
candied_orange

7

Anda dapat mengganti hampir semua metodologi dengan metodologi lain dan masih menghasilkan perangkat lunak yang berfungsi. Namun beberapa lebih cocok untuk masalah tertentu daripada yang lain.

Itu tergantung pada banyak hal mana yang lebih disukai. Seni sebelumnya dalam aplikasi, pengalaman dalam tim, perkembangan masa depan yang diharapkan, preferensi pribadi dan betapa sulitnya bagi pendatang baru untuk mengatasinya, untuk beberapa nama.

Ketika Anda menjadi lebih berpengalaman dan lebih sering bergulat dengan kreasi orang lain, Anda mungkin akan lebih menekankan pada kontemplasi terakhir.

Warisan masih merupakan alat pemodelan yang valid dan kuat yang tidak selalu selalu yang paling fleksibel tetapi menawarkan panduan yang kuat untuk orang-orang baru yang mungkin berterima kasih atas pemetaan yang jelas ke domain masalah.


-1

Warisan tidak sepenting yang pernah diduga. Itu masih penting , dan menghapusnya akan menjadi kesalahan buruk.

Contoh ekstrem adalah Objective-C di mana semuanya adalah subkelas dari NSObject (kompiler sebenarnya tidak memungkinkan Anda untuk mendeklarasikan kelas yang tidak memiliki baseclass, jadi segala sesuatu yang tidak dibangun ke dalam kompiler haruslah subkelas dari sesuatu dibangun ke dalam kompiler). Ada banyak hal berguna yang dibangun di dalam NSObject yang tidak akan Anda lewatkan.

Contoh menarik lainnya adalah UIView, kelas "tampilan" dasar di UIKit untuk pengembangan iOS. Ini adalah kelas yang di satu sisi sedikit seperti kelas abstrak yang menyatakan fungsionalitas yang seharusnya diisi oleh subclass, tetapi juga berguna sendiri. Ini memiliki subclass yang dipasok oleh UIKit, yang sering digunakan sebagaimana adanya. Ada komposisi oleh pengembang yang memasang subview dalam tampilan. Dan sering ada subclass yang ditentukan pengembang, yang sering menggunakan komposisi. Tidak ada aturan tunggal yang ketat atau bahkan aturan, Anda menggunakan apa pun yang paling sesuai dengan kebutuhan Anda.


"Contoh ekstrem" Anda kira-kira bagaimana fungsi sebagian besar bahasa OO modern: subclass Python type, setiap non-primitif di Jawa mewarisi dari Object, segala sesuatu dalam JavaScript memiliki prototipe yang berakhir dengan Object, dll.
Delioth

-1

[Aku akan membahayakan jawaban yang nakal untuk pertanyaan tituler.]

Bisakah kita sepenuhnya mengganti warisan menggunakan pola strategi dan injeksi ketergantungan?

Ya ... kecuali pola strategi itu sendiri menggunakan warisan. Pola strategi bekerja pada pewarisan antarmuka. Mengganti warisan dengan komposisi + strategi memindahkan warisan ke tempat yang berbeda. Meskipun demikian, penggantian seperti itu sering kali layak dilakukan, karena memungkinkan untuk memisahkan hierarki .


2
+ Msgstr "Pola strategi bekerja pada pewarisan antarmuka ". Ingat, pola strategi adalah pola desain; bukan pola implementasi. Jadi itu dapat diimplementasikan menggunakan antarmuka, tetapi juga dapat diimplementasikan menggunakan misalnya hash / kamus fungsi.
David Arno

3
Warisan antarmuka sama sekali bukan warisan, itu hanya kontrak atau klasifikasi. Anda dapat menggunakan delegasi juga untuk pola strategi (saya lebih suka menggunakannya).
Deepak Mishra

Plus satu, Bung: ... bekerja pada pewarisan antarmuka , pujian atas penggunaan yang tepat dari istilah umum "antarmuka." Saya pikir desain kelas yang buruk atau tidak kompeten adalah alasan dasar pertanyaan ini muncul. Untuk menjelaskan mengapa / bagaimana mengambil sebuah buku; iblis ada dalam perinciannya. Saya telah bekerja dengan desain warisan yang menyenangkan untuk dilihat dan pada saat yang sama warisan mendalam yang merupakan neraka. Saya telah melihat interfacedesain craptastic diperbaiki dengan warisan. Masalah tersembunyi di sini adalah implementasi yang tidak konsisten implementasi morphing behaviour. P, S. warisan & antarmuka tidak saling eksklusif,
radarbob

@DavidArno komentar : Komentar ini akan baik-baik saja jika dilampirkan pada pertanyaan OP. Pertanyaan OP secara teknis salah disebutkan tetapi kami masih mendapatkannya. Masalahnya bukan jawaban khusus ini.
radarbob

@DeepakMishra komentar: . "Antarmuka" adalah semua anggota publik dari suatu kelas. Sayangnya makna "antarmuka" kelebihan beban. Kita harus berhati-hati untuk membedakan makna umum dengan kata kunci bahasa pemrogramaninterface
radarbob

-2

Tidak. Jika bebek mallard membutuhkan parameter yang berbeda untuk instantiate daripada bebek jenis lainnya, itu akan menjadi mimpi buruk untuk berubah. Anda juga harus bisa instantiate bebek? Ini lebih seperti bebek mallard atau bebek Mandarin atau jenis bebek apa pun yang Anda miliki.

Juga saya mungkin ingin beberapa logika di sekitar tipe sehingga tipe hierarki mungkin lebih baik.

Sekarang jika penggunaan kembali kode menjadi bermasalah untuk beberapa fungsionalitas, kita dapat menyusunnya dengan meneruskan fungsi-fungsi melalui konstruktor.

Sekali lagi, itu sangat tergantung pada kasus penggunaan Anda. Jika Anda hanya memiliki bebek dan bebek mallard, hierarki kelas adalah solusi yang lebih sederhana.

Tapi di sini adalah contoh di mana Anda ingin menggunakan pola strategi: Jika Anda memiliki kelas pelanggan, dan Anda ingin meneruskan strategi penagihan (antarmuka) yang bisa berupa strategi penagihan jam senang atau strategi penagihan biasa, Anda bisa melewatinya di konstruktor. Dengan cara ini Anda tidak perlu membuat dua kelas pelanggan yang berbeda dan Anda tidak perlu hierarki. Anda hanya memiliki satu kelas pelanggan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.