Apakah pewarisan berganda melanggar Prinsip Tanggung Jawab Tunggal?

18

Jika Anda memiliki kelas yang mewarisi dari dua kelas yang berbeda, bukankah ini berarti bahwa subkelas Anda secara otomatis melakukan (setidaknya) 2 hal, satu dari setiap superclass?

Saya percaya tidak ada perbedaan jika Anda memiliki beberapa antarmuka warisan.

Sunting: Agar jelas, saya percaya bahwa jika subkelas banyak kelas melanggar SRP, maka mengimplementasikan beberapa (non-penanda atau antarmuka dasar (misalnya Sebanding)) antarmuka juga melanggar SRP.

m3th0dman
sumber
Untuk lebih jelasnya, Anda juga percaya mengimplementasikan beberapa antarmuka melanggar SRP?
Saya percaya bahwa jika beberapa kelas subkelas melanggar SRP, maka mengimplementasikan beberapa antarmuka (non-marker) juga melanggar SRP.
m3th0dman
Saya akan menjawab pertanyaan ini, tetapi Anda telah menyertakan pendapat Anda dalam pertanyaan itu, yang tidak saya setujui.
Nicole
1
@NickC: Saya belum mengatakan pendapat saya (Anda harus membaca komentar di atas); Saya akan mengedit pertanyaan untuk membuatnya lebih jelas.
m3th0dman
1
@NickC Saya tidak punya pendapat, itu sebabnya saya bertanya. Saya hanya mengatakan bahwa keduanya terkait (warisan antarmuka dan warisan kelas); jika itu melanggar untuk warisan kelas maka itu melanggar untuk antarmuka juga, jika tidak untuk satu maka tidak melanggar untuk yang lain. Dan saya tidak peduli dengan suara yang naik ...
m3th0dman

Jawaban:

17

Dalam arti yang sangat sempit, jawabannya adalah "Ya": dengan asumsi bahwa kelas dasar atau antarmuka Anda dirancang untuk satu tujuan, mewarisi keduanya menciptakan kelas dengan banyak tanggung jawab. Namun, apakah itu "hal buruk" tergantung pada sifat dari kelas atau antarmuka yang Anda warisi.

Anda dapat mempartisi kelas dan antarmuka Anda menjadi dua kelompok besar - yang membahas kompleksitas esensial sistem Anda, dan yang membahas kompleksitas yang tidak disengaja. Jika mewarisi dari lebih dari satu kelas "kompleksitas esensial", itu buruk; jika Anda mewarisi dari satu kelas "esensial" dan satu atau lebih "tidak disengaja", tidak apa-apa.

Misalnya, dalam sistem penagihan, Anda dapat memiliki kelas untuk mewakili faktur dan siklus penagihan (mereka mengatasi kompleksitas esensial) dan kelas untuk objek yang bertahan lama (mereka membahas kompleksitas yang tidak disengaja). Jika Anda mewarisi seperti ini

class BillingCycleInvoice : public BillingCycle, public Invoice {
};

itu buruk: Anda BillingCycleInvoicememiliki tanggung jawab yang beragam karena berkaitan dengan kompleksitas esensial dari sistem.

Di sisi lain, jika Anda mewarisi seperti ini

class PersistentInvoice : public Invoice, public PersistentObject {
};

kelas Anda OK: secara teknis, ini melayani dua masalah sekaligus, tetapi karena hanya satu di antaranya yang penting, Anda dapat menghapus warisan yang tidak disengaja sebagai "biaya berbisnis".

dasblinkenlight
sumber
3
Contoh kedua Anda sangat mirip dengan apa yang akan menjadi perhatian lintas sektoral dalam pemrograman berorientasi aspek. Ini mengatakan bahwa objek Anda kadang-kadang perlu melakukan hal-hal (seperti logging, tes kontrol akses, atau bertahan) yang bukan fokus utamanya, dan bahwa Anda dapat melakukannya dengan banyak pewarisan. Itu penggunaan alat yang tepat.
Yang paling sederhana adalah Jika mengimplementasikan antarmuka baru harus menyelesaikan fungsionalitas kelas maka tidak. Tetapi jika itu menambahkan tanggung jawab baru, Implementasikan antarmuka dengan beberapa kelas lain, maka menyuntikkan sebagai ketergantungan tidak akan pernah melanggar SRP
Ramankingdom
9

SRP adalah pedoman untuk menghindari kelas dewa, yang buruk, tetapi juga bisa dianggap terlalu harfiah dan proyek memulai balon dengan banyak kelas yang tidak dapat benar-benar melakukan apa-apa tanpa sedikit pun digabungkan. Multiple inheritance / multiple interfaces dapat menjadi pertanda kelas menjadi terlalu besar, tetapi itu juga bisa menjadi pertanda bahwa fokus Anda terlalu terperinci untuk tugas yang dihadapi dan solusi Anda terlalu direkayasa, atau bisa juga dengan sempurna kasus penggunaan yang valid untuk pewarisan berganda dan kekhawatiran Anda tidak berdasar.

Desain perangkat lunak adalah seni dan ilmu pengetahuan, yang merupakan tindakan penyeimbang dari banyak kepentingan yang bersaing. Kita memiliki aturan untuk membantu menghindari hal-hal yang kita tahu jauh di satu arah yang menyebabkan masalah, tetapi aturan itu bisa dengan mudah membawa kita ke tempat yang sama buruk atau lebih buruk daripada apa yang kita coba hindari di tempat pertama.

Ryathal
sumber
4

Agak. Mari kita fokus pada prinsip utama untuk SRP:

Kelas seharusnya hanya memiliki satu alasan untuk berubah.

Warisan berganda tidak memaksakan ini, tetapi cenderung mengarah ke sana. Jika kelas menimpa atau mengimplementasikan kelas dasarnya, itu cenderung menjadi lebih banyak alasan untuk mengubahnya. Dengan cara ini, interface multiple inheritance cenderung lebih merepotkan daripada class inheritance. Jika dua kelas diwarisi tetapi tidak diganti, maka alasan untuk mengubah ada di kelas dasar bukan kelas baru.

Tempat-tempat di mana pewarisan berganda tidak melanggar SRP adalah ketika semua kecuali satu dari tipe dasar bukanlah sesuatu yang diterapkan oleh kelas, tetapi bertindak sebagai sifat yang dimiliki oleh kelas tersebut. Pertimbangkan IDisposabledalam C #. Hal-hal yang sekali pakai tidak memiliki dua tanggung jawab, antarmuka hanya bertindak sebagai pandangan ke kelas yang memiliki sifat tersebut tetapi memiliki tanggung jawab yang jelas berbeda.

Telastyn
sumber
2

Tidak menurut saya. Bagi saya, satu tanggung jawab adalah "memodelkan pengguna aplikasi saya". Saya mungkin perlu mengirimkan data itu melalui layanan web, dan menyimpannya dalam basis data relasional. Saya bisa menyediakan fungsionalitas itu dengan mewarisi beberapa kelas campuran.

Itu tidak berarti kelas memiliki tiga tanggung jawab. Ini berarti bahwa bagian dari tanggung jawab untuk memodelkan pengguna memerlukan serialisasi dan kegigihan yang mendukung.

kevin cline
sumber
2

Tidak semuanya. Di Jawa, Anda dapat memiliki antarmuka yang mewakili semacam objek domain - Foo, misalnya. Mungkin perlu dibandingkan dengan objek Foo lainnya, dan itu mengimplementasikan Comparable (dengan logika perbandingan dieksternalisasi dalam kelas Comparator); mungkin objek ini juga perlu Serializable, sehingga dapat diangkut melalui jaringan; dan mungkin harus Cloneable. Dengan demikian kelas mengimplementasikan tiga antarmuka tetapi tidak memiliki logika di dalamnya untuk mendukung mereka di luar yang didukung oleh Foo.

Yang mengatakan, itu konyol untuk mengambil SRP secara ekstrem. Jika kelas mendefinisikan lebih dari satu metode, apakah itu melanggar SRP? Semua Objek Java memiliki metode toString () dan metode equals (Obyek), yang berarti SETIAP objek di Jawa bertanggung jawab atas persamaan dan representasi diri. Itu adalah hal yang buruk?

Matthew Flynn
sumber
1

Saya kira itu tergantung pada bagaimana Anda mendefinisikan tanggung jawab. Dalam contoh iostream klasik itu tidak melanggar SRP. IOstream bertanggung jawab untuk mengelola satu aliran dua arah.

Setelah Anda kehabisan tanggung jawab primitif, Anda beralih ke tanggung jawab tingkat tinggi yang lebih umum, setelah semua bagaimana Anda mendefinisikan tanggung jawab titik masuk utama Anda? Tanggung jawabnya harus 'menjadi titik masuk utama' bukan 'semua yang dilakukan aplikasi saya' jika tidak, tidak mungkin untuk tidak melanggar SRP dan menghasilkan aplikasi.

stonemetal
sumber