Berbagai kasus penggunaan pewarisan

15

Java menghilangkan banyak pewarisan dengan alasan bahwa ia menghilangkan tujuan desain untuk menjaga bahasa tetap sederhana .

Saya ingin tahu apakah Java (dengan sistem ramah lingkungannya) benar-benar "sederhana". Python tidak kompleks dan memiliki banyak pewarisan. Jadi tanpa terlalu subjektif, pertanyaan saya adalah ...

Apa pola masalah khas yang mendapat manfaat dari kode yang dirancang untuk memanfaatkan banyak pewarisan berganda

treecoder
sumber
8
Java menghilangkan banyak sekali hal-hal yang sangat baik dengan alasan yang sangat sedikit. Saya tidak akan mengharapkan pembenaran yang layak untuk MI.
DeadMG
2
Warisan berganda Python jelas merupakan wilayah di sini-menjadi-naga . Fakta bahwa ia menggunakan resolusi nama mendalam-pertama, kiri-ke-kanan memiliki masalah yang signifikan untuk pemeliharaan dan pemahaman. Meskipun dapat berguna dalam hierarki kelas yang dangkal, ini bisa menjadi sangat kontra-intuitif dalam yang mendalam.
Mark Booth
Saya pikir alasan bahwa Java tidak mengandung banyak pewarisan adalah bahwa pengembang Java ingin bahasa mereka mudah dipelajari. Warisan berganda, walaupun sangat kuat dalam beberapa kasus, sulit untuk dipahami, dan bahkan lebih sulit untuk digunakan dengan efek yang baik; itu bukan hal yang Anda inginkan untuk menghadapi mahasiswa baru pemrograman. Maksud saya: Bagaimana Anda menjelaskan warisan virtual kepada seseorang yang berjuang dengan konsep warisan itu sendiri? Dan, karena multiple inheritance juga tidak sepenuhnya sepele di sisi implementator, kemungkinan dev Jawa yang mengabaikannya adalah win-win.
cmaster - mengembalikan monica
Java diketik secara nominal. Python bukan. Ini membuat banyak pewarisan lebih mudah diterapkan dan dipahami dengan Python.
Jules

Jawaban:

11

Pro:

  1. Kadang-kadang memungkinkan pemodelan masalah yang lebih jelas daripada cara lain untuk memodelkannya.
  2. Jika orangtua yang berbeda memiliki tujuan ortogonal, itu dapat memungkinkan beberapa jenis penggabungan

Cons:

  1. Jika orang tua yang berbeda tidak memiliki tujuan ortogonal, itu membuat tipe ini sulit untuk dipahami.
  2. Tidak mudah untuk memahami bagaimana itu diterapkan dalam suatu bahasa (bahasa apa pun).

Dalam C ++ contoh bagus dari multiple inheritance yang digunakan untuk fitur orthogonal komposit adalah ketika Anda menggunakan CRTP untuk, misalnya, menyiapkan sistem komponen untuk gim.

Saya sudah mulai menulis contoh tapi saya pikir contoh dunia nyata lebih layak untuk dilihat. Beberapa kode Ogre3D menggunakan banyak pewarisan dengan cara yang bagus dan sangat intuitif. Sebagai contoh, kelas Mesh mewarisi dari Resources dan AnimationContainer. Sumber daya mengekspos antarmuka umum untuk semua sumber daya dan AnimationContainer mengekspos antarmuka khusus untuk memanipulasi serangkaian animasi. Mereka tidak terkait, jadi mudah untuk berpikir tentang Mesh sebagai sumber daya yang di samping itu dapat menampung serangkaian animasi. Terasa alami bukan?

Anda dapat melihat contoh lain di perpustakaan ini , seperti cara alokasi memori dikelola dengan cara butiran halus dengan membuat kelas mewarisi dari varian kelas CRTP kelebihan beban baru dan hapus.

Seperti yang dikatakan, masalah utama dengan pewarisan berganda muncul dari pencampuran konsep terkait. Itu membuat bahasa harus mengatur implementasi yang kompleks (lihat cara C ++ memungkinkan untuk bermain dengan masalah berlian ...) dan pengguna tidak yakin apa yang terjadi dalam implementasi itu. Misalnya, baca artikel ini menjelaskan bagaimana ini diterapkan di C ++ .

Menghapusnya dari bahasa membantu menghindari orang-orang yang tidak tahu bagaimana bahasa tersebut membuat informasi menjadi buruk. Tapi itu memaksa untuk berpikir dengan cara yang, kadang-kadang, tidak terasa alami, bahkan jika itu kasus tepi, itu terjadi lebih sering yang mungkin Anda pikirkan.

Klaim
sumber
saya akan sangat menghargai jika Anda menghiasi jawaban Anda dengan contoh masalah - yang akan membuat istilah seperti "tujuan ortogonal" menjadi lebih jelas - tetapi terima kasih
treecoder
Ok, izinkan saya mencoba menambahkan sesuatu.
Klaim
Ogre3D bukan tempat yang saya inginkan untuk inspirasi desain - apakah Anda pernah melihat infeksi Singleton mereka?
DeadMG
Pertama, pewaris tunggal bukanlah benar-benar seorang singleton, konstruksi dan penghancurannya eksplisit. Selanjutnya, Ogre adalah lapisan di atas sistem perangkat keras (atau driver grafis jika Anda mau). Itu berarti harus ada hanya satu representasi unik untuk antarmuka sistem (seperti Root atau yang lain). Mereka dapat menghapus beberapa singleton tetapi bukan itu intinya di sini. Saya secara sukarela menghindari hal ini untuk menghindari diskusi troll, jadi tolong, lihat contoh yang saya tunjukkan. Penggunaan Singleton mereka mungkin tidak sempurna tetapi jelas berguna dalam praktiknya (tetapi hanya untuk sistem mereka, bukan segalanya).
Klaim
4

Ada konsep yang disebut mixin yang banyak digunakan dalam bahasa yang lebih dinamis. Multiple inheritance adalah salah satu cara di mana mixin dapat didukung oleh suatu bahasa. Mixin umumnya digunakan sebagai cara bagi kelas untuk mengakumulasi berbagai fungsi. Tanpa pewarisan berganda, Anda harus menggunakan agregasi / delegasi untuk mendapatkan perilaku tipe mixin dengan kelas, yang sedikit lebih sintaksisnya.

RasionalGeek
sumber
+1 ini sebenarnya merupakan alasan bagus untuk memiliki banyak pewarisan. Mixin membawa konotasi tambahan ("kelas ini tidak boleh digunakan sebagai yang berdiri sendiri")
ashes999
2

Saya pikir pilihannya terutama didasarkan pada masalah karena masalah berlian .

Selain itu, sering kali mungkin untuk menghindari penggunaan pewarisan berganda dengan delegasi atau cara lain.

Saya tidak yakin tentang arti pertanyaan terakhir Anda. Tetapi jika itu "dalam kasus mana pewarisan berganda berguna?", Maka dalam semua kasus di mana Anda ingin memiliki objek A yang memiliki fungsi objek B dan C, pada dasarnya.

dagnelies
sumber
2

Saya tidak akan mempelajari banyak hal di sini, tetapi Anda pasti dapat memahami banyak warisan dalam python melalui tautan berikut http://docs.python.org/release/1.5.1p1/tut/multiple.html :

Satu-satunya aturan yang diperlukan untuk menjelaskan semantik adalah aturan resolusi yang digunakan untuk referensi atribut kelas. Ini adalah kedalaman-pertama, kiri-ke-kanan. Jadi, jika sebuah atribut tidak ditemukan di DerivedClassName, itu dicari di Base1, lalu (secara rekursif) di kelas dasar dari Base1, dan hanya jika tidak ditemukan di sana, itu dicari di Base2, dan seterusnya.

...

Jelas bahwa penggunaan sembarang pewarisan berganda adalah mimpi buruk pemeliharaan, mengingat ketergantungan pada Python pada konvensi untuk menghindari konflik nama yang tidak disengaja. Masalah terkenal dengan multiple inheritance adalah kelas yang berasal dari dua kelas yang kebetulan memiliki kelas dasar yang sama. Meskipun cukup mudah untuk mengetahui apa yang terjadi dalam kasus ini (instance akan memiliki satu salinan `variabel instan 'atau atribut data yang digunakan oleh kelas dasar umum), tidak jelas bahwa semantik ini dengan cara apa pun berguna.

Ini hanya paragraf kecil tapi cukup besar untuk menghapus keraguan yang kurasa.

Pankaj Upadhyay
sumber
1

Satu tempat di mana pewarisan berganda akan berguna adalah situasi di mana sebuah kelas mengimplementasikan beberapa antarmuka, tetapi Anda ingin memiliki beberapa fungsi bawaan bawaan untuk setiap antarmuka. Ini berguna jika sebagian besar kelas yang mengimplementasikan beberapa antarmuka ingin melakukan sesuatu dengan cara yang sama, tetapi kadang-kadang Anda perlu melakukan sesuatu yang berbeda. Anda dapat memiliki masing-masing kelas dengan implementasi yang sama, tetapi lebih masuk akal untuk mendorongnya ke satu lokasi.

KeithB
sumber
1
Apakah itu memerlukan pewarisan berganda yang digeneralisasi, atau sekadar sarana yang dengannya antarmuka dapat menentukan perilaku default untuk metode yang tidak diterapkan? Jika antarmuka hanya dapat menentukan implementasi standar untuk metode yang mereka implementasikan sendiri (berbeda dengan yang mereka warisi dari antarmuka lain) fitur seperti itu akan sepenuhnya menghindari masalah berlian ganda yang membuat banyak pewarisan menjadi sulit.
supercat
1

Apa pola masalah khas yang mendapat manfaat dari kode yang dirancang untuk menggunakan banyak pewarisan berganda?

Ini hanya satu contoh tapi saya rasa sangat berharga untuk meningkatkan keselamatan dan mengurangi godaan untuk menerapkan perubahan cascading di seluruh penelepon atau subkelas.

Di mana saya telah menemukan banyak pewarisan yang sangat berguna bahkan untuk antarmuka yang paling abstrak dan stateless adalah idiom antarmuka non-virtual (NVI) dalam C ++.

Mereka bahkan tidak benar - benar abstrak kelas dasar seperti antarmuka yang hanya memiliki sedikit implementasi bagi mereka untuk menegakkan aspek universal dari kontrak mereka, karena mereka tidak benar-benar mempersempit generalitas kontrak sehingga lebih baik menegakkannya .

Contoh sederhana (beberapa orang mungkin memeriksa apakah suatu pegangan file yang lewat terbuka atau semacamnya):

// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
    // Pre: x should always be greater than or equal to zero.
    void f(int x) /*final*/
    {
        // Make sure x is actually greater than or equal to zero
        // to meet the necessary pre-conditions of this function.
        assert(x >= 0);

        // Call the overridden function in the subtype.
        f_impl(x);
    }

protected:
    // Overridden by a boatload of subtypes which implement
    // this non-virtual interface.
    virtual void f_impl(int x) = 0;
};

Dalam hal ini, mungkin fdisebut oleh seribu tempat dalam basis kode, sementara f_implditimpa oleh seratus subclass.

Akan sulit untuk melakukan pemeriksaan keamanan semacam ini di semua 1000 tempat yang memanggil fatau semua 100 tempat yang mengesampingkan f_impl.

Dengan hanya membuat titik entri ini ke fungsionalitas nonvirtual, itu memberi saya satu tempat sentral untuk melakukan pemeriksaan ini. Dan pemeriksaan ini tidak mengurangi abstraksi sedikit pun, karena hanya menyatakan prasyarat yang diperlukan untuk memanggil fungsi ini. Dalam arti tertentu, ini bisa saja memperkuat kontrak yang disediakan oleh antarmuka, dan menghilangkan beban memeriksa xinput untuk memastikan itu sesuai dengan prasyarat yang valid di semua 100 tempat yang menimpanya.

Ini adalah sesuatu yang saya harap setiap bahasa miliki, dan juga saya harapkan, bahkan dalam C ++, bahwa itu sedikit lebih merupakan konsep asli (mis: tidak mengharuskan kita untuk mendefinisikan fungsi terpisah untuk menimpanya).

Ini sangat berguna jika Anda tidak melakukan ini assertsebelumnya, dan menyadari bahwa Anda membutuhkannya nanti ketika beberapa tempat acak dalam basis kode menemukan nilai negatif yang diteruskan f.


sumber
0

Pertama: beberapa salinan kelas dasar (masalah C ++) & kopling ketat antara kelas dasar dan turunan.

Kedua: multiple inheritance dari interface abstrak

quant_dev
sumber
apakah Anda menyarankan itu tidak berguna dalam konteks apa pun? Dan semuanya bisa dirancang / dikodekan dengan nyaman tanpa itu? Juga tolong jelaskan pada poin kedua.
treecoder