Komposisi atas warisan tetapi

13

Saya mencoba mengajari diri saya sendiri rekayasa perangkat lunak dan menghadapi beberapa informasi yang saling bertentangan yang membingungkan saya.

Saya telah belajar OOP dan apa kelas abstrak / Antarmuka dan bagaimana menggunakannya, tapi kemudian saya membaca bahwa orang harus 'lebih menyukai komposisi daripada warisan'. Saya mengerti komposisi adalah ketika satu kelas menyusun / membuat objek kelas lain untuk memanfaatkan / berinteraksi dengan fungsionalitas objek baru itu.

Jadi pertanyaan saya adalah ... Apakah saya seharusnya tidak menggunakan kelas dan antarmuka abstrak? Untuk tidak membuat kelas abstrak dan memperluas / mewarisi fungsi kelas abstrak itu di kelas konkret, dan sebagai gantinya, hanya menyusun objek baru untuk menggunakan fungsionalitas kelas lain?

Atau, apakah saya seharusnya menggunakan komposisi DAN mewarisi dari kelas abstrak; menggunakan keduanya bersamaan satu sama lain? Jika demikian, dapatkah Anda memberikan beberapa contoh bagaimana itu akan bekerja dan manfaatnya?

Karena saya paling nyaman dengan PHP, saya menggunakannya untuk meningkatkan keterampilan OOP / SE saya sebelum pindah ke bahasa lain dan mentransfer keterampilan SE saya yang baru didapat, jadi contoh menggunakan PHP akan sangat dihargai.

MikeMason
sumber
2
"Komposisi budi atas warisan" adalah ide konyol yang persis sama artinya dengan "bujukan gergaji daripada bor." Mereka adalah dua alat yang berbeda yang tepat untuk digunakan dalam dua kasus penggunaan yang berbeda, dan saat-saat ketika Anda benar-benar dapat menggunakan salah satu dari keduanya secara bergantian sebenarnya sangat jarang.
Mason Wheeler
2
"Mendukung X daripada Y" tidak berarti tidak pernah menggunakan Y, pikirkan saja jika X akan lebih baik
Richard Tingle
2
"Favor komposisi daripada warisan" lebih akurat dinyatakan sebagai "Tidak pernah, pernah menggunakan pewarisan kelas", dengan klarifikasi bahwa mengimplementasikan antarmuka bukanlah warisan dan jika bahasa pilihan Anda hanya mendukung kelas abstrak, bukan antarmuka, mewarisi dari 100% kelas abstrak dapat dilihat sebagai "bukan warisan" juga.
David Arno
1
@MasonWheeler, tidak ada kasus penggunaan yang valid untuk warisan. Setidaknya sebagai "jahat" fitur sebagai "goto".
David Arno
1
@ DavidArno Itu benar-benar konyol. Warisan adalah salah satu fitur paling bermanfaat dan produktif yang pernah dikembangkan dalam seluruh sejarah pemrograman. Seperti apa pun yang bermanfaat, ada banyak cara untuk menyalahgunakannya, tetapi menggunakannya dengan benar secara besar-besaran meningkatkan kekuatan dan produktivitas pekerjaan Anda.
Mason Wheeler

Jawaban:

19

Komposisi atas Inheritance berarti bahwa ketika Anda ingin menggunakan kembali atau memperluas fungsionalitas kelas yang ada, seringkali lebih tepat untuk membuat kelas lain yang akan 'membungkus' kelas yang ada dan menggunakannya secara internal. Pola dekorator adalah contohnya.

Warisan bukanlah cara standar yang baik untuk menangani skenario penggunaan ulang karena Anda sering ingin menggunakan hanya sebagian dari fungsi kelas dasar dan subkelas tidak dapat mendukung seluruh kontrak dari kelas dasar dengan cara yang memenuhi substitusi Liskov prinsip .

Metode template adalah contoh yang baik dari kasus di mana pewarisan sesuai.

Lihat jawaban ini untuk panduan tentang cara memilih antara komposisi dan warisan.

astreltsov
sumber
+1 untuk menunjukkan bahwa dalam kasus penggunaan kembali sebagian kelas, bagian yang tidak digunakan lebih bersih diabaikan dengan komposisi daripada dengan warisan.
Lawrence
4

Saya tidak cukup akrab dengan PHP untuk memberi Anda contoh nyata dalam bahasa itu, tetapi berikut adalah beberapa panduan yang menurut saya bermanfaat.

Antarmuka / sifat berguna untuk mendefinisikan perilaku di banyak kelas yang mungkin memiliki sedikit kesamaan.

Misalnya, jika Anda bekerja pada api JSON, Anda dapat mendefinisikan antarmuka yang menentukan dua metode: "toJson" dan "toStatusCode". Ini memungkinkan Anda untuk menulis helper yang dapat mengambil objek apa pun yang mengimplementasikan antarmuka ini, dan mengubahnya menjadi respons Http.

Sebagian besar waktu, ini lebih disukai daripada warisan langsung, karena cenderung menderita masalah kelas dasar yang rapuh, karena cenderung ke arah hierarki kelas yang dangkal.

Warisan Langsung paling baik dianggap sebagai sesuatu yang Anda lakukan ketika ada alasan kuat untuk melakukannya, daripada sesuatu yang Anda lakukan kecuali ada alasan kuat untuk tidak melakukannya.

Dalam bahasa yang tidak mendukung implementasi standar dalam antarmuka, ini mungkin merupakan cara yang masuk akal untuk berbagi sekumpulan fungsionalitas dasar, tetapi biasanya sangat membatasi apa yang dapat Anda lakukan dengan subkelas (beberapa pewarisan, jika tersedia, jarang sepadan dengan rasa sakitnya) . Seringkali, saya merasa perlu menulis metode redirect boilerplate untuk menggunakan komposisi.

Komposisi harus menjadi strategi default Anda. Sebisa mungkin, buat dengan antarmuka, dan berikan sebagai argumen konstruktor. Ini akan membuat hidup Anda jauh lebih mudah saat menulis tes.

Hindari bekerja dengan lajang global sebanyak mungkin, karena membandingkan output yang diharapkan dan aktual adalah rasa sakit yang tepat jika bagian dari output tergantung pada keadaan jam sistem.

Ini, tentu saja, tidak selalu memungkinkan. Sebagai contoh, kerangka kerja Play menyediakan pustaka JSON yang pada dasarnya adalah bahasa khusus domain. Mengubah ini akan menjadi tugas besar, di mana, mengganti panggilan ke 'Json.prettyPrint' hanya akan menjadi bagian yang sangat kecil.

Morgen
sumber
1

Komposisi adalah ketika kelas menawarkan beberapa fungsionalitas dengan membuat instance kelas (mungkin internal) yang sudah mengimplementasikan fungsionalitas ini, alih-alih mewarisi dari kelas itu.

Jadi, misalnya, jika Anda memiliki kelas yang memodelkan sebuah kapal, dan Anda sekarang diberitahu bahwa kapal Anda harus menawarkan helipad, itu tidak wajar untuk menurunkan kapal Anda dari helipad, (ya!) Sebaliknya, Anda harus memiliki kapal Anda mengandung kelas helipad dan memaparkannya melalui beberapa Ship.getHelipad()metode.

Pada tahun-tahun yang lalu (satu dekade yang lalu) orang biasa memandang pewarisan sebagai cara cepat dan mudah untuk menggabungkan fungsi, jadi ada banyak contoh jenis "kapal yang mewarisi helipad", yang tentu saja sangat timpang.

Tetapi diktum "Komposisi Yang Disukai atas Warisan" telah diucapkan dengan hati-hati untuk memperjelas bahwa ini hanyalah sebuah saran, bukan aturan. Penulis diktum itu cukup hati-hati untuk tidak mengatakan sesuatu seperti "kamu tidak akan pernah menggunakan warisan, hanya komposisi". Ini pada dasarnya membawa perhatian komunitas rekayasa perangkat lunak fakta bahwa warisan telah digunakan secara berlebihan, sementara dalam banyak kasus, komposisi menghasilkan desain yang lebih jelas, elegan, dan dapat dipertahankan daripada warisan.

Jadi, pada dasarnya, diktum "Komposisi Keberanian atas Warisan" menunjukkan bahwa setiap kali Anda dihadapkan dengan "mewarisi atau menulis?" pertanyaan, Anda harus berpikir keras apa strategi yang paling cocok, dan bahwa sebagian besar kemungkinan adalah bahwa strategi yang paling cocok akan berubah menjadi komposisi, bukan warisan.

Tetapi karena ini bukan aturan, Anda juga harus ingat bahwa ada banyak kasus di mana warisan lebih alami. Jika Anda menggunakan komposisi di sana di mana Anda seharusnya menggunakan warisan, banyak kejahatan akan menimpa kode Anda.

Untuk kembali ke contoh kapal, jika kapal Anda perlu menawarkan antarmuka untuk berinteraksi dengan FloatingMachine, maka lebih alami untuk menurunkannya dari FloatingMachinekelas abstrak , yang sangat mungkin pada gilirannya diturunkan dari Machinekelas abstrak lain .

Berikut ini adalah aturan praktis untuk jawaban pertanyaan komposisi vs pewarisan:

Apakah kelas saya memiliki hubungan "adalah" dengan antarmuka yang perlu diungkapkan? Jika ya, gunakan warisan. Jika tidak, gunakan komposisi.

Kapal "adalah" mesin apung, dan mesin apung "adalah" mesin. Jadi, warisan baik-baik saja bagi mereka. Tapi kapal bukan, tentu saja, helipad. Jadi lebih baik menyusun fungsi helipad.

Mike Nakis
sumber