Dari mana konsep "komposisi bantuan daripada warisan" ini berasal?

143

Dalam beberapa bulan terakhir, mantra "komposisi kesukaan daripada warisan" tampaknya telah muncul entah dari mana dan menjadi semacam meme dalam komunitas pemrograman. Dan setiap kali saya melihatnya, saya sedikit bingung. Itu seperti seseorang berkata, "Latihan budi daripada palu." Dalam pengalaman saya, komposisi dan warisan adalah dua alat yang berbeda dengan kasus penggunaan yang berbeda, dan memperlakukan mereka seolah-olah mereka dipertukarkan dan satu secara inheren lebih unggul dari yang lain tidak masuk akal.

Juga, saya tidak pernah melihat penjelasan nyata mengapa pewarisan itu buruk dan komposisinya bagus, yang hanya membuat saya lebih curiga. Apakah itu seharusnya diterima dengan iman? Substitusi dan polimorfisme Liskov memiliki manfaat yang terkenal dan jelas, dan IMO mencakup seluruh titik penggunaan pemrograman berorientasi objek, dan tidak ada yang pernah menjelaskan mengapa mereka harus dibuang demi komposisi.

Adakah yang tahu dari mana konsep ini berasal, dan apa alasan di baliknya?

Mason Wheeler
sumber
45
Seperti yang orang lain tunjukkan, sudah ada sejak lama - saya terkejut Anda baru saja mendengarnya. Ini intuitif bagi siapa saja yang telah membangun sistem besar dalam bahasa seperti Java untuk waktu berapa pun. Merupakan inti dari setiap wawancara yang pernah saya berikan dan ketika seorang kandidat mulai berbicara tentang warisan, saya mulai meragukan tingkat keterampilan dan jumlah pengalaman mereka. Berikut ini adalah pengantar yang baik mengapa pewarisan adalah solusi yang rapuh (ada banyak lainnya): artima.com/lejava/articles/designprinciples4.html
Janx
6
@ Janx: Mungkin itu saja. Saya tidak membangun sistem besar dalam bahasa seperti Jawa; Saya membangunnya di Delphi, dan tanpa substitusi dan polimorfisme Liskov kami tidak akan pernah menyelesaikan apa pun. Model objeknya berbeda dalam hal-hal tertentu daripada Java atau C ++, dan banyak masalah yang tampaknya ingin diselesaikan oleh pepatah ini tidak benar-benar ada, atau jauh lebih tidak bermasalah, di Delphi. Perspektif berbeda dari sudut pandang berbeda, saya kira.
Mason Wheeler
14
Saya menghabiskan beberapa tahun di tim yang membangun sistem yang relatif besar di Delphi dan pohon-pohon warisan yang tinggi tentu saja menggigit tim kami dan menyebabkan kami sangat kesakitan. Saya menduga bahwa perhatian Anda pada prinsip-prinsip SOLID membantu Anda menghindari masalah areass, bukan penggunaan Delphi.
Bevan
8
Beberapa bulan terakhir?!?
Jas
6
IMHO konsep itu belum pernah sepenuhnya disesuaikan dengan berbagai bahasa yang mendukung kedua warisan antarmuka (yaitu, subtyping dengan antarmuka murni) dan warisan implementasi. Terlalu banyak orang mengikuti mantra ini dan tidak menggunakan antarmuka yang cukup.
Uri

Jawaban:

136

Meskipun saya pikir saya sudah mendengar diskusi komposisi-vs-warisan jauh sebelum GoF, saya tidak bisa meletakkan jari saya pada sumber tertentu. Lagipula mungkin Booch.

<rant>

Ah tapi seperti banyak mantra, yang ini telah merosot di sepanjang garis khas:

  1. itu diperkenalkan dengan penjelasan dan argumen terperinci oleh sumber yang sangat dihormati yang membuat ungkapan-tangkapan sebagai pengingat dari diskusi kompleks asli
  2. itu dibagikan dengan mengetahui bagian-dari-the-club mengedipkan mata oleh beberapa di-the-know untuk sementara waktu, umumnya ketika mengomentari kesalahan n00b
  3. segera itu diulang tanpa berpikir oleh ribuan demi ribuan yang tidak pernah membaca penjelasan, tetapi suka menggunakannya sebagai alasan untuk tidak berpikir , dan sebagai cara yang murah dan mudah untuk merasa superior dari orang lain
  4. pada akhirnya, tidak ada jumlah sanggahan yang masuk akal yang dapat membendung gelombang "meme" - dan paradigma itu berubah menjadi agama dan dogma.

Meme itu, awalnya dimaksudkan untuk mengarahkan n00bs ke pencerahan, sekarang digunakan sebagai klub untuk memukul mereka secara tidak sadar.

Komposisi dan pewarisan adalah hal-hal yang sangat berbeda, dan tidak boleh dikacaukan satu sama lain. Meskipun benar bahwa komposisi dapat digunakan untuk mensimulasikan pewarisan dengan banyak pekerjaan tambahan , ini tidak menjadikan warisan sebagai warga negara kelas dua, juga tidak menjadikan komposisi sebagai putra favorit. Fakta bahwa banyak n00bs mencoba menggunakan pewarisan sebagai jalan pintas tidak membatalkan mekanisme, dan hampir semua n00b belajar dari kesalahan dan dengan demikian meningkat.

Silakan BERPIKIR tentang desain Anda, dan berhenti semburan semboyan.

</rant>

Steven A. Lowe
sumber
16
Booch berpendapat bahwa warisan implementasi memperkenalkan kopling tinggi di antara kelas-kelas. Di sisi lain, ia menganggap pewarisan konsep kunci yang membedakan OOP dari pemrograman prosedural.
Nemanja Trifunovic
13
Seharusnya ada kata untuk hal semacam ini. Regurgitasi Dini , mungkin?
Jörg W Mittag
8
@ Jörg: Anda tahu apa yang akan terjadi pada istilah seperti Regurgitasi Dini? Persis seperti yang dijelaskan di atas. :) (Btw. Kapan regurgitasi tidak pernah prematur?)
Bjarke Freund-Hansen
6
@Nemanja: Benar. Pertanyaannya adalah apakah sambungan ini benar-benar buruk. Jika kelas secara kuat berpasangan dan secara konseptual membentuk hubungan supertipe-subtipe, dan tidak akan pernah dapat dipisahkan secara sementara bahkan jika mereka dipisahkan secara formal pada tingkat bahasa, maka penggandengan yang kuat baik-baik saja.
dsimcha
5
Mantra ini sederhana, "hanya karena Anda dapat membentuk play-doh agar terlihat seperti apa pun tidak berarti bahwa Anda harus membuat semuanya dari play-doh." ;)
Evan Plaice
73

Pengalaman.

Seperti yang Anda katakan mereka adalah alat untuk pekerjaan yang berbeda, tetapi frasa muncul karena orang tidak menggunakannya dengan cara itu.

Warisan terutama merupakan alat polimorfik, tetapi beberapa orang, banyak yang akan menanggung risiko kemudian, mencoba menggunakannya sebagai cara menggunakan kembali / berbagi kode. Alasannya adalah "baik jika saya mewarisi maka saya mendapatkan semua metode gratis", tetapi mengabaikan fakta bahwa dua kelas ini berpotensi tidak memiliki hubungan polimorfik.

Jadi mengapa lebih menyukai komposisi daripada pewarisan - yah hanya karena lebih sering hubungan antar kelas bukanlah hubungan polimorfik. Itu ada hanya untuk membantu mengingatkan orang untuk tidak menanggapi sentakan dengan mewarisi.

Stephen Bailey
sumber
7
Jadi pada dasarnya, Anda tidak bisa mengatakan "Anda seharusnya tidak menggunakan OOP di tempat pertama jika Anda tidak mengerti substitusi Liskov," jadi Anda mengatakan "mendukung komposisi daripada warisan" sebagai upaya untuk membatasi kerusakan yang dilakukan oleh tidak kompeten coders?
Mason Wheeler
13
@Mason: Seperti mantra apa pun, "komposisi kesukaan daripada warisan" ditargetkan pada programmer pemula. Jika Anda sudah tahu kapan harus menggunakan warisan dan kapan harus menggunakan komposisi maka tidak ada gunanya mengulang mantra seperti itu.
Dean Harding
7
@Dean - Saya tidak yakin pemula sama dengan orang yang tidak memahami praktik terbaik pewarisan. Saya pikir ada lebih dari itu. Warisan yang buruk adalah penyebab dari banyak, banyak sakit kepala dalam pekerjaan saya dan itu bukan kode yang ditulis oleh programmer yang akan dianggap "pemula".
Nicole
6
@Renesis: Hal pertama yang saya pikirkan ketika saya mendengar itu adalah, "beberapa orang memiliki pengalaman sepuluh tahun, dan beberapa orang memiliki pengalaman satu tahun yang diulang sepuluh kali."
Mason Wheeler
8
Saya merancang proyek yang cukup besar setelah OO "Mendapatkan" pertama dan sangat bergantung padanya. Walaupun itu bekerja dengan baik, saya terus-menerus menemukan desain rapuh - menyebabkan iritasi kecil di sana-sini dan kadang-kadang membuat refactor tertentu menjadi pelacur lengkap. Agak sulit untuk menjelaskan seluruh pengalaman, tetapi frasa "Favour Compisition over Inheritence" menggambarkannya dengan PERSIS. Perhatikan bahwa itu tidak mengatakan atau bahkan menyiratkan "Hindari Warisan", itu hanya memberi Anda sedikit dorongan ketika pilihannya tidak jelas.
Bill K
43

Ini bukan ide baru, saya percaya itu sebenarnya diperkenalkan dalam buku pola desain GoF, yang diterbitkan pada tahun 1994.

Masalah utama dengan warisan adalah bahwa itu kotak putih. Menurut definisi, Anda perlu mengetahui detail implementasi dari kelas yang Anda warisi. Dengan komposisi, di sisi lain, Anda hanya peduli dengan antarmuka publik dari kelas yang Anda buat.

Dari buku GoF:

Warisan memperlihatkan subclass ke detail implementasi orang tuanya, sering dikatakan bahwa 'warisan memecah enkapsulasi'

Artikel wikipedia di buku GoF memiliki pengantar yang layak.

Dean Harding
sumber
14
Saya tidak setuju dengan itu. Anda tidak perlu tahu detail implementasi dari kelas asal Anda; hanya anggota publik dan dilindungi yang terpapar oleh kelas. Jika Anda harus mengetahui detail implementasi, maka Anda atau siapa pun yang menulis kelas dasar melakukan kesalahan, dan jika cacatnya ada di kelas dasar, komposisi tidak akan membantu Anda memperbaiki / menyelesaikannya.
Mason Wheeler
18
Bagaimana Anda bisa tidak setuju dengan sesuatu yang belum Anda baca? Ada satu halaman yang padat dan setengah dari diskusi dari GoF bahwa Anda hanya mendapatkan pandangan kecil.
philosodad
7
@pholosodad: Saya tidak setuju dengan sesuatu yang belum saya baca. Saya tidak setuju dengan apa yang ditulis Dean, bahwa "Secara definisi, Anda perlu mengetahui detail implementasi dari kelas yang Anda warisi," yang telah saya baca.
Mason Wheeler
10
Apa yang saya tulis hanyalah ringkasan dari apa yang dijelaskan dalam buku GoF. Saya mungkin telah mengatakannya sedikit dengan kuat (Anda tidak perlu tahu semua detail implementasi) tetapi itulah alasan umum mengapa GoF mengatakan untuk memilih komposisi daripada warisan.
Dean Harding
2
Koreksi saya jika saya salah tetapi saya melihatnya sebagai, "mendukung hubungan kelas eksplisit (yaitu Antarmuka) daripada implisit (yaitu warisan)." Yang pertama memberi tahu Anda apa yang Anda butuhkan tanpa memberi tahu Anda cara melakukannya. Yang terakhir tidak hanya memberi tahu Anda bagaimana melakukannya tetapi akan membuat Anda menyesalinya.
Evan Plaice
26

Untuk menjawab bagian dari pertanyaan Anda, saya percaya konsep ini pertama kali muncul dalam Pola Desain GOF : Elemen buku Software Berorientasi Objek Reusable , yang pertama kali diterbitkan pada tahun 1994. Ungkapan tersebut muncul di bagian atas halaman 20, dalam pengantar:

Menyukai komposisi objek dibandingkan pewarisan

Mereka mengawali pernyataan ini dengan perbandingan singkat tentang warisan dengan komposisi. Mereka tidak mengatakan "tidak pernah menggunakan warisan".

vjones
sumber
23

"Komposisi atas warisan" adalah cara pendek (dan tampaknya menyesatkan) untuk mengatakan "Ketika merasa bahwa data (atau perilaku) suatu kelas harus dimasukkan ke dalam kelas lain, selalu pertimbangkan untuk menggunakan komposisi sebelum menerapkan warisan secara membabi buta".

Mengapa ini benar? Karena warisan menciptakan kopling waktu kompilasi yang ketat antara 2 kelas. Komposisi dalam kontras adalah kopling longgar, yang antara lain memungkinkan pemisahan keprihatinan yang jelas, kemungkinan beralih ketergantungan pada saat runtime dan lebih mudah, lebih mudah diuji ketergantungan ketergantungan.

Itu hanya berarti warisan harus ditangani dengan hati-hati karena harus dibayar mahal, bukan karena tidak berguna. Sebenarnya, "Komposisi atas warisan" sering berakhir dengan "Komposisi + pewarisan atas warisan" karena Anda sering ingin ketergantungan yang Anda buat menjadi superclass abstrak daripada subkelas beton itu sendiri. Ini memungkinkan Anda untuk beralih di antara implementasi konkret berbeda dari ketergantungan Anda saat runtime.

Untuk alasan itu (antara lain), Anda mungkin akan melihat pewarisan yang lebih sering digunakan dalam bentuk implementasi antarmuka atau kelas abstrak daripada pewarisan vanila.

Contoh (metaforis) dapat berupa:

"Saya memiliki kelas Snake dan saya ingin memasukkan sebagai bagian dari kelas itu apa yang terjadi ketika Snake menggigit. Saya akan tergoda untuk memiliki Ular mewarisi kelas BiterAnimal yang memiliki metode Bite () dan menimpa metode itu untuk mencerminkan gigitan berbisa Tapi Komposisi atas Warisan memperingatkan saya bahwa saya harus mencoba menggunakan komposisi sebagai gantinya ... Dalam kasus saya, ini dapat diterjemahkan ke dalam Snake yang memiliki anggota Bite. Kelas Bite dapat berupa abstrak (atau antarmuka) dengan beberapa subclass. Ini akan memungkinkan saya hal-hal yang baik seperti memiliki subkelas VenomousBite dan DryBite dan dapat mengubah gigitan pada contoh ular yang sama dengan usia ular yang bertambah. Ditambah menangani semua efek dari Gigitan di kelas terpisahnya sendiri dapat memungkinkan saya untuk menggunakannya kembali di kelas Frost , karena gigitan es tetapi bukan BiterAnimal, dan seterusnya ... "

guillaume31
sumber
4
Contoh yang sangat bagus dengan Ular. Saya bertemu kasus serupa baru-baru ini dengan kelas saya sendiri
Maksee
22

Beberapa kemungkinan argumen untuk komposisi:

Komposisi sedikit lebih banyak bahasa / kerangka kerja
Warisan agnostik dan apa yang ditegakkan / butuhkan / aktifkan akan berbeda antara bahasa dalam hal apa sub / superclass memiliki akses ke dan apa implikasi kinerja itu mungkin memiliki metode virtual dll. Komposisi cukup mendasar dan membutuhkan sangat sedikit dukungan bahasa, dan dengan demikian implementasi lintas platform / kerangka kerja yang berbeda dapat berbagi pola komposisi dengan lebih mudah.

Komposisi adalah cara yang sangat sederhana dan taktil untuk membangun objek
Warisan relatif mudah dipahami, tetapi masih tidak mudah ditunjukkan dalam kehidupan nyata. Banyak objek dalam kehidupan nyata dapat dipecah menjadi beberapa bagian dan tersusun. Katakanlah sepeda dapat dibangun menggunakan dua roda, bingkai, kursi, rantai dll. Mudah dijelaskan dengan komposisi. Sedangkan dalam metafora pewarisan, Anda bisa mengatakan bahwa sepeda memanjang dengan sepatu roda, agak layak tetapi masih jauh dari gambaran sebenarnya daripada komposisi (jelas ini bukan contoh pewarisan yang sangat baik, tetapi intinya tetap sama). Bahkan kata pewarisan (setidaknya dari sebagian besar penutur bahasa Inggris AS yang saya harapkan) secara otomatis memanggil makna di sepanjang baris "Sesuatu diturunkan dari kerabat yang sudah meninggal" yang memiliki beberapa korelasi dengan artinya dalam perangkat lunak, tetapi masih hanya cocok longgar.

Komposisi hampir selalu lebih fleksibel.
Menggunakan komposisi Anda selalu dapat memilih untuk mendefinisikan perilaku Anda sendiri atau hanya mengekspos bagian itu dari bagian-bagian yang Anda buat. Dengan cara ini Anda tidak menghadapi batasan apa pun yang mungkin dikenakan oleh hierarki warisan (virtual vs non-virtual, dll.)

Jadi, bisa jadi karena Komposisi secara alami adalah metafora yang lebih sederhana yang memiliki kendala teoretis lebih sedikit daripada pewarisan. Lebih lanjut, alasan-alasan khusus ini mungkin lebih jelas selama waktu desain, atau mungkin menonjol ketika berhadapan dengan beberapa titik sakit dari warisan.

Penafian:
Jelas ini bukan jalan yang jelas. Setiap desain pantas dievaluasi beberapa pola / alat. Warisan banyak digunakan, memiliki banyak manfaat dan berkali-kali lebih elegan dari komposisi. Ini hanya beberapa kemungkinan alasan yang bisa digunakan ketika memilih komposisi.

TJB
sumber
1
"Jelas itu bukan jalan yang jelas ini / jalan satu arah." Kapan komposisi tidak lebih (atau sama) fleksibel daripada warisan? Saya berpendapat bahwa itu adalah jalan satu arah. Warisan hanyalah gula sintaksis untuk kasus komposisi khusus.
weberc2
Komposisi seringkali tidak fleksibel ketika menggunakan bahasa yang mengimplementasikan komposisi menggunakan antarmuka yang diimplementasikan secara eksplisit , karena antarmuka tersebut tidak diperbolehkan untuk berevolusi dengan cara yang kompatibel dari waktu ke waktu. #jmtcw
MikeSchinkel
11

Mungkin Anda baru saja memperhatikan orang mengatakan ini dalam beberapa bulan terakhir, tetapi telah diketahui oleh programmer yang baik lebih lama dari itu. Saya tentu mengatakannya di mana yang sesuai selama sekitar satu dekade.

Inti dari konsep ini adalah bahwa ada overhead konseptual yang besar untuk warisan. Ketika Anda menggunakan warisan, maka setiap panggilan metode tunggal memiliki pengiriman implisit di dalamnya. Jika Anda memiliki pohon warisan yang dalam, atau banyak pengiriman, atau (bahkan lebih buruk) keduanya, maka mencari tahu di mana metode tertentu akan dikirim dalam panggilan tertentu dapat menjadi PITA kerajaan. Itu membuat alasan yang benar tentang kode lebih kompleks, dan itu membuat debugging lebih sulit.

Biarkan saya memberikan contoh sederhana untuk diilustrasikan. Misalkan jauh di dalam pohon warisan, seseorang bernama metode foo. Kemudian orang lain datang dan menambahkan foodi atas pohon, tetapi melakukan sesuatu yang berbeda. (Kasus ini lebih umum dengan pewarisan berganda.) Sekarang orang yang bekerja di kelas akar telah melanggar kelas anak yang tidak jelas dan mungkin tidak menyadarinya. Anda dapat memiliki cakupan 100% dengan tes unit dan tidak melihat kerusakan ini karena orang di atas tidak akan berpikir untuk menguji kelas anak, dan tes untuk kelas anak tidak berpikir untuk menguji metode baru yang dibuat di atas . (Memang ada cara untuk menulis tes unit yang akan menangkap ini, tetapi ada juga kasus di mana Anda tidak dapat dengan mudah menulis tes seperti itu.)

Sebaliknya ketika Anda menggunakan komposisi, pada setiap panggilan biasanya lebih jelas apa tujuan pengiriman Anda. (Oke, jika Anda menggunakan inversi kontrol, misalnya dengan injeksi ketergantungan, mencari tahu kemana panggilan juga bisa menjadi masalah. Tapi biasanya lebih mudah untuk mencari tahu.) Ini membuat alasan tentang hal itu lebih mudah. Sebagai bonus, komposisi menghasilkan metode terpisah satu sama lain. Contoh di atas tidak boleh terjadi di sana karena kelas anak akan pindah ke beberapa komponen yang tidak jelas, dan tidak pernah ada pertanyaan tentang apakah panggilan ke fooditujukan untuk komponen yang tidak jelas atau objek utama.

Sekarang Anda benar sekali bahwa warisan dan komposisi adalah dua alat yang sangat berbeda yang melayani dua jenis benda yang berbeda. Memang warisan membawa overhead konseptual, tetapi ketika itu adalah alat yang tepat untuk pekerjaan itu, ia membawa overhead konseptual yang lebih sedikit daripada mencoba untuk tidak menggunakannya dan lakukan dengan tangan apa fungsinya untuk Anda. Tidak ada yang tahu apa yang mereka lakukan akan mengatakan bahwa Anda tidak boleh menggunakan warisan. Tetapi pastikan itu adalah hal yang benar untuk dilakukan.

Sayangnya banyak pengembang belajar tentang perangkat lunak berorientasi objek, belajar tentang warisan, dan kemudian pergi menggunakan kapak baru mereka sesering mungkin. Yang berarti bahwa mereka mencoba menggunakan warisan di mana komposisi adalah alat yang tepat. Semoga mereka akan belajar lebih baik pada waktunya, tetapi seringkali ini tidak terjadi sampai setelah beberapa anggota badan diangkat, dll. Memberitahu mereka di muka bahwa itu adalah ide yang buruk cenderung mempercepat proses belajar dan mengurangi cedera.

btilly
sumber
3
Baiklah, saya kira itu masuk akal dari perspektif C ++. Itu adalah sesuatu yang tidak pernah saya pikirkan, karena ini bukan masalah di Delphi, yang sering saya gunakan. (Tidak ada pewarisan berganda, dan jika Anda memiliki metode di kelas dasar dan metode lain dengan nama yang sama di kelas turunan yang tidak menggantikan metode dasar, kompiler akan mengeluarkan peringatan, sehingga Anda tidak secara tidak sengaja berakhir dengan masalah seperti ini.)
Mason Wheeler
1
@ Alasan: Versi Ander dari Object Pascal (alias Delphi) lebih unggul daripada C ++ dan Java ketika menyangkut masalah pewarisan kelas dasar yang rapuh. Tidak seperti C ++ dan Java, overloading metode virtual yang diwarisi tidak tersirat.
bit-twiddler
2
@ bit-twiddler, apa yang Anda katakan tentang C ++ dan Java dapat dikatakan tentang Smalltalk, Perl, Python, Ruby, JavaScript, Common Lisp, Objective-C, dan setiap bahasa lain yang saya pelajari yang menyediakan segala bentuk dukungan OO. Yang mengatakan, googling menunjukkan bahwa C # mengikuti jejak Object Pascal.
btilly
3
Itu karena Anders Hejlsberg mendesain rasa Borland dari Object Pascal (alias Delphi) dan C #.
bit-twiddler
8

Ini adalah reaksi terhadap pengamatan bahwa pemula OO cenderung menggunakan warisan ketika mereka tidak perlu. Warisan tentu bukan hal yang buruk, tetapi bisa digunakan secara berlebihan. Jika satu kelas hanya membutuhkan fungsionalitas dari yang lain, maka komposisi mungkin akan berfungsi. Dalam keadaan lain, warisan akan berfungsi dan komposisinya tidak.

Mewarisi dari kelas menyiratkan banyak hal. Ini menyiratkan bahwa Turunan adalah jenis Basis (lihat prinsip Pergantian Liskov untuk detail berdarah), di mana setiap kali Anda menggunakan Basis itu akan masuk akal untuk menggunakan Turunan. Ini memberikan akses yang diturunkan ke anggota yang dilindungi dan fungsi anggota Base. Ini adalah hubungan yang erat, artinya memiliki kopling yang tinggi, dan perubahan yang satu lebih cenderung membutuhkan perubahan yang lain.

Coupling adalah hal yang buruk. Itu membuat program lebih sulit untuk dipahami dan dimodifikasi. Hal lain dianggap sama, Anda harus selalu memilih opsi dengan kopling lebih sedikit.

Oleh karena itu, jika salah satu komposisi atau warisan akan melakukan pekerjaan secara efektif, pilih komposisi, karena kopling lebih rendah. Jika komposisi tidak akan melakukan pekerjaan dengan efektif, dan warisan akan, pilihlah warisan, karena Anda harus.

David Thornley
sumber
"Dalam keadaan lain, warisan akan bekerja dan komposisinya tidak akan." Kapan? Warisan dapat dimodelkan menggunakan komposisi dan antarmuka polimorfisme, jadi saya akan terkejut jika ada keadaan di mana komposisi tidak akan bekerja.
weberc2
8

Inilah dua sen saya (di luar semua poin bagus yang telah diangkat):

IMHO, ini bermuara pada kenyataan bahwa sebagian besar programmer tidak benar-benar mendapatkan warisan, dan akhirnya berlebihan, sebagian sebagai hasil dari bagaimana konsep ini diajarkan. Konsep ini ada sebagai cara untuk mencoba mencegah mereka dari melakukannya secara berlebihan, alih-alih berfokus pada mengajar mereka bagaimana melakukannya dengan benar.

Siapa pun yang telah menghabiskan waktu mengajar atau membimbing tahu bahwa inilah yang sering terjadi, terutama dengan pengembang baru yang memiliki pengalaman dalam paradigma lain:

Pengembang ini awalnya merasa bahwa warisan adalah konsep yang menakutkan ini. Jadi mereka akhirnya membuat jenis dengan tumpang tindih antarmuka (misalnya, perilaku yang ditentukan sama tanpa subtyping umum), dan dengan global untuk menerapkan fungsionalitas umum.

Kemudian (sering sebagai hasil dari pengajaran yang terlalu bersemangat), mereka belajar tentang manfaat warisan, tetapi sering diajarkan sebagai solusi terbaik untuk digunakan kembali. Mereka berakhir dengan persepsi bahwa perilaku bersama harus dibagi melalui warisan. Ini karena fokusnya sering pada implementasi warisan daripada subtyping.

Dalam 80% kasus itu cukup baik. Tetapi 20% lainnya adalah di mana masalahnya dimulai. Demi menghindari penulisan ulang dan untuk memastikan mereka telah mengambil keuntungan dari implementasi bersama, mereka mulai merancang hierarki mereka di sekitar implementasi yang dimaksud daripada abstraksi yang mendasarinya. "Stack mewarisi dari daftar tertaut ganda" adalah contoh yang bagus untuk ini.

Sebagian besar guru tidak bersikeras memperkenalkan konsep antarmuka pada titik ini, karena ini merupakan konsep lain, atau karena (dalam C ++) Anda harus memalsukan mereka menggunakan kelas abstrak dan pewarisan berganda yang tidak diajarkan pada tahap ini. Di Jawa, banyak guru tidak membedakan "no multiple inheritance" atau "multiple inheritance evil" dari pentingnya interface.

Semua ini diperparah oleh fakta bahwa kita telah belajar banyak tentang keindahan tidak harus menulis kode berlebihan dengan warisan implementasi, sehingga banyak kode delegasi langsung terlihat tidak wajar. Jadi komposisi terlihat berantakan, itulah sebabnya kita perlu aturan praktis ini untuk mendorong programmer baru untuk menggunakannya (yang mereka lakukan juga).

Uri
sumber
Itu bagian nyata dari pendidikan. Anda harus melakukan kursus yang lebih tinggi untuk mendapatkan yaitu sisa 20% tetapi Anda, sebagai siswa, baru saja diajarkan kursus dasar dan dan mungkin menengah. Pada akhirnya, Anda merasa diajar dengan baik karena Anda melakukan kursus dengan baik (dan tidak melihatnya hanya dengan jepit di tangga). Ini hanya bagian dari kenyataan dan taruhan terbaik kami adalah memahami efek sampingnya, bukan menyerang coders.
Independen
8

Dalam salah satu komentar, Mason menyebutkan bahwa suatu hari kita akan berbicara tentang Warisan yang dianggap berbahaya .

Saya berharap begitu.

Masalah dengan pewarisan sekaligus sederhana, dan mematikan, itu tidak menghormati gagasan bahwa satu konsep harus memiliki satu fungsi.

Di sebagian besar OO-bahasa, ketika mewarisi dari kelas dasar Anda:

  • mewarisi dari antarmuka
  • mewarisi dari implementasinya (data dan metode)

Dan di sini menjadi masalah.

Ini bukan satu-satunya pendekatan, meskipun bahasa 00 sebagian besar terjebak dengan itu. Untungnya antarmuka / kelas abstrak ada di dalamnya.

Juga, kurangnya kemudahan untuk melakukan sebaliknya berkontribusi membuatnya sebagian besar digunakan: terus terang, bahkan mengetahui hal ini, akankah Anda mewarisi dari antarmuka dan menanamkan kelas dasar dengan komposisi, mendelegasikan sebagian besar panggilan metode? Akan jauh lebih baik, Anda bahkan akan diperingatkan jika tiba-tiba sebuah metode baru muncul di antarmuka dan harus memilih, secara sadar, bagaimana menerapkannya.

Sebagai titik tandingan, Haskellhanya izinkan untuk menggunakan Prinsip Liskov ketika "diturunkan" dari antarmuka murni (disebut konsep) (1). Anda tidak dapat berasal dari kelas yang ada, hanya komposisi yang memungkinkan Anda untuk menyematkan datanya.

(1) konsep dapat memberikan default yang masuk akal untuk implementasi, tetapi karena mereka tidak memiliki data, default ini hanya dapat didefinisikan dalam hal metode lain yang diusulkan oleh konsep atau dalam hal konstanta.

Matthieu M.
sumber
7

Jawaban sederhananya adalah: warisan memiliki penggandaan lebih besar dari komposisi. Diberikan dua opsi, dengan kualitas yang setara, pilih salah satu yang kurang digabungkan.

Jeffrey Faust
sumber
2
Tapi itulah intinya. Mereka tidak "setara." Warisan memungkinkan substitusi dan polimorfisme Liskov, yang merupakan inti dari penggunaan OOP. Komposisi tidak.
Mason Wheeler
4
Polimorfisme / LSP bukan "titik utama" dari OOP, mereka adalah satu fitur. Ada juga enkapsulasi, abstraksi, dll. Pewarisan menyiratkan suatu hubungan "adalah", model-model perjanjian "hubungan".
Steve
@Steve: Anda dapat melakukan abstraksi dan enkapsulasi dalam paradigma apa pun yang mendukung pembuatan struktur data dan prosedur / fungsi. Tapi polimorfisme (khusus mengacu pada metode pengiriman virtual dalam konteks ini) dan substitusi Liskov adalah unik untuk pemrograman berorientasi objek. Itulah yang saya maksud dengan itu.
Mason Wheeler
3
@ Alasan: Pedoman ini adalah untuk "mendukung komposisi daripada warisan." Sama sekali tidak berarti ini tidak menggunakan atau bahkan menghindari warisan. Semua itu mengatakan bahwa ketika semua hal lain sama, pilih komposisi. Tetapi jika Anda membutuhkan warisan, gunakan itu!
Jeffrey Faust
7

Saya pikir saran semacam ini seperti mengatakan "lebih suka mengemudi daripada terbang". Artinya, pesawat memiliki segala macam keunggulan dibandingkan mobil, tetapi itu datang dengan kompleksitas tertentu. Jadi, jika banyak orang mencoba terbang dari pusat kota ke pinggiran kota, saran yang benar-benar perlu mereka dengar adalah bahwa mereka tidak perlu terbang, dan pada kenyataannya, terbang hanya akan membuatnya lebih rumit dalam jangka panjang, bahkan jika tampaknya keren / efisien / mudah dalam jangka pendek. Sedangkan bila Anda tidak perlu terbang, itu umumnya seharusnya jelas.

Demikian juga, warisan dapat melakukan hal-hal komposisi tidak bisa, tetapi Anda harus menggunakannya ketika Anda membutuhkannya, dan bukan ketika Anda tidak. Jadi, jika Anda tidak pernah tergoda untuk hanya menganggap Anda membutuhkan warisan ketika Anda tidak, maka Anda tidak perlu saran "lebih suka komposisi". Tetapi banyak orang melakukannya , dan memang membutuhkan nasihat itu.

Seharusnya secara implisit bahwa ketika Anda benar-benar TIDAK membutuhkan warisan, sudah jelas, dan Anda harus menggunakannya kemudian.

Juga, jawaban Steven Lowe. Sungguh, sungguh itu.

Jack V.
sumber
1
Saya suka analogi Anda
barrylloyd
2

Warisan pada dasarnya tidak buruk, dan komposisi pada dasarnya tidak baik. Mereka hanyalah alat yang dapat digunakan programmer OO untuk merancang perangkat lunak.

Ketika Anda melihat kelas, apakah ia melakukan lebih dari yang seharusnya ( SRP )? Apakah itu menduplikasi fungsi yang tidak perlu ( KERING ), atau itu terlalu tertarik pada properti atau metode kelas lain ( Fitur Envy ) ?. Jika kelas tersebut melanggar semua konsep ini (dan mungkin lebih), apakah ia berusaha menjadi Kelas Dewa . Ini adalah sejumlah masalah yang dapat terjadi ketika mendesain perangkat lunak, tidak ada yang merupakan masalah warisan, tetapi yang dapat dengan cepat membuat sakit kepala besar dan ketergantungan yang rapuh di mana polimorfisme juga telah diterapkan.

Akar masalahnya kemungkinan adalah kurangnya pemahaman tentang pewarisan, dan lebih dari salah satu pilihan yang buruk dalam hal desain, atau mungkin tidak mengenali "bau kode" yang berkaitan dengan kelas yang tidak mematuhi Prinsip Tanggung Jawab Tunggal . Substitusi Polimorfisme dan Liskov tidak perlu dibuang demi komposisi. Polimorfisme itu sendiri dapat diterapkan tanpa bergantung pada warisan, ini semua adalah konsep yang cukup komplementer. Jika diterapkan dengan penuh pertimbangan. Kuncinya adalah bertujuan untuk menjaga kode Anda sederhana, dan bersih, dan untuk tidak menyerah terlalu khawatir tentang jumlah kelas yang perlu Anda buat untuk membuat desain sistem yang kuat.

Sejauh masalah komposisi lebih disukai daripada warisan, ini benar-benar hanya kasus lain dari aplikasi bijaksana dari elemen desain yang paling masuk akal dalam hal masalah yang sedang dipecahkan. Jika Anda tidak perlu mewarisi perilaku, maka sebaiknya Anda tidak melakukannya karena komposisi akan membantu menghindari ketidakcocokan dan upaya refactoring besar di kemudian hari. Jika di sisi lain Anda menemukan bahwa Anda mengulang banyak kode sehingga semua duplikasi berpusat pada sekelompok kelas yang sama, maka mungkin menciptakan nenek moyang yang sama akan membantu mengurangi jumlah panggilan dan kelas yang identik Anda mungkin perlu mengulang di antara setiap kelas. Dengan demikian, Anda menyukai komposisi, namun Anda tidak menganggap bahwa warisan tidak pernah berlaku.

S.Robins
sumber
-1

Saya percaya, kutipan yang sebenarnya datang dari Joshua Bloch's "Java Efektif", di mana itu adalah judul dari salah satu bab. Dia mengklaim bahwa warisan aman dalam sebuah paket dan di mana pun kelas telah dirancang dan didokumentasikan secara khusus untuk perpanjangan. Dia mengklaim, seperti orang lain telah mencatat, bahwa warisan istirahat enkapsulasi karena subkelas tergantung pada detail implementasi superclass-nya. Ini, katanya, mengarah pada kerapuhan.

Walaupun dia tidak menyebutkannya, bagi saya kelihatannya satu warisan di Jawa berarti komposisi memberi Anda lebih banyak fleksibilitas daripada warisan.

Vanessa Williams
sumber