Java pomija wielokrotne dziedziczenie, ponieważ pomija cel projektowy polegający na uproszczeniu języka .
Zastanawiam się, czy Java (ze swoim ekosystemem) jest naprawdę „prosta”. Python nie jest złożony i ma wielokrotne dziedziczenie. Więc nie będąc zbyt subiektywnym, moje pytanie brzmi ...
Jakie są typowe wzorce problemów, które korzystają z kodu zaprojektowanego do częstego korzystania z wielokrotnego dziedziczenia
multiple-inheritance
treecoder
źródło
źródło
Odpowiedzi:
Plusy:
Cons :
W C ++ dobrym przykładem wielokrotnego dziedziczenia stosowanego do złożonych funkcji ortogonalnych jest użycie CRTP do, na przykład, skonfigurowania systemu składowego gry.
Zacząłem pisać przykład, ale wydaje mi się, że warto przyjrzeć się przykładowi z prawdziwego świata. Niektóre kody Ogre3D używają wielokrotnego dziedziczenia w przyjemny i bardzo intuicyjny sposób. Na przykład klasa Mes dziedziczy zarówno Resources, jak i AnimationContainer. Zasoby udostępniają interfejs wspólny dla wszystkich zasobów, a AnimationContainer udostępnia interfejs specyficzny dla manipulowania zestawem animacji. Nie są ze sobą powiązane, więc łatwo jest myśleć o Siatce jako o zasobie, który dodatkowo może zawierać zestaw animacji. Czy to naturalne, prawda?
Możesz spojrzeć na inne przykłady w tej bibliotece , takie jak sposób zarządzania alokacją pamięci w sposób drobnoziarnisty, powodując, że klasy dziedziczą po wariantach przeciążenia klasy CRTP nowym i usuwanym .
Jak powiedziano, główne problemy związane z wielokrotnym dziedziczeniem wynikają z mieszania powiązanych pojęć. To sprawia, że język musi ustawiać złożone implementacje (zobacz, w jaki sposób C ++ pozwala grać z problemem z diamentem ...), a użytkownik nie jest pewien, co się dzieje w tej implementacji. Na przykład przeczytaj ten artykuł wyjaśniający, w jaki sposób jest on implementowany w C ++ .
Usunięcie go z języka pomaga unikać ludzi, którzy nie wiedzą, w jaki sposób ten język jest zmuszony, aby coś złego uczynić. Ale zmusza do myślenia w sposób, który czasami nie wydaje się naturalny, nawet jeśli są to skrajne przypadki, zdarza się częściej, niż myślisz.
źródło
Istnieje koncepcja zwana mixinami, która jest powszechnie używana w bardziej dynamicznych językach. Wielokrotne dziedziczenie to jeden ze sposobów, w jaki miksy mogą być obsługiwane przez język. Mieszanki są na ogół używane jako sposób na zgromadzenie przez klasę różnych funkcji. Bez wielokrotnego dziedziczenia musisz użyć agregacji / delegacji, aby uzyskać zachowanie typu mixin z klasą, która jest nieco bardziej złożona.
źródło
Myślę, że wybór opiera się głównie na problemach związanych z problemem diamentu .
Ponadto często można obejść stosowanie wielokrotnego dziedziczenia przez przekazanie uprawnień lub w inny sposób.
Nie jestem pewien znaczenia twojego ostatniego pytania. Ale jeśli jest to „w jakich przypadkach przydatne jest wielokrotne dziedziczenie?”, To we wszystkich przypadkach, w których chciałbyś, aby obiekt A miał w zasadzie funkcje obiektów B i C.
źródło
Nie będę tutaj zagłębiał się, ale z pewnością można zrozumieć wielokrotne dziedziczenie w pythonie za pomocą następującego linku http://docs.python.org/release/1.5.1p1/tut/multiple.html :
...
To tylko mały akapit, ale wystarczająco duży, aby wyjaśnić wątpliwości.
źródło
Jednym z miejsc, w których przydałoby się wielokrotne dziedziczenie, jest sytuacja, w której klasa implementuje kilka interfejsów, ale chciałbyś mieć jakąś domyślną funkcjonalność wbudowaną w każdy interfejs. Jest to przydatne, jeśli większość klas implementujących jakiś interfejs chce zrobić coś w ten sam sposób, ale czasami trzeba zrobić coś innego. Możesz mieć każdą klasę z tą samą implementacją, ale sensowniej jest umieścić ją w jednym miejscu.
źródło
To tylko jeden przykład, ale uważam go za nieoceniony w celu poprawy bezpieczeństwa i złagodzenia pokus, aby zastosować zmiany kaskadowe w dzwoniących lub podklasach.
Tam, gdzie znalazłem wielokrotne dziedziczenie, niezwykle przydatne nawet dla najbardziej abstrakcyjnych, bezstanowych interfejsów, jest nie-wirtualny idiom interfejsu (NVI) w C ++.
Nie są nawet tak naprawdę abstrakcyjnymi klasami podstawowymi , jak interfejsy, które mają tylko odrobinę implementacji, aby egzekwować uniwersalne aspekty swoich umów, ponieważ tak naprawdę nie zawężają ogólności umowy, a jedynie lepiej ją egzekwują. .
Prosty przykład (niektórzy mogą sprawdzić, czy przekazany uchwyt pliku jest otwarty, czy coś takiego):
W tym przypadku być może
f
nazywa je tysiąc miejsc w bazie kodu, af_impl
zastępuje je sto podklas.Trudno byłoby przeprowadzić tego rodzaju kontrolę bezpieczeństwa we wszystkich 1000 wzywających miejscach
f
lub we wszystkich 100 nadrzędnych miejscachf_impl
.Po prostu uczynienie tego punktu wejścia funkcją niewirtualną, daje mi jedno centralne miejsce do przeprowadzenia tej kontroli. Ta kontrola nie zmniejsza w najmniejszym stopniu abstrakcji, ponieważ po prostu zapewnia warunek konieczny do wywołania tej funkcji. W pewnym sensie jest to prawdopodobnie wzmocnienie umowy zapewnianej przez interfejs i zmniejszenie obciążenia związanego z sprawdzaniem
x
danych wejściowych, aby upewnić się, że są one zgodne z prawidłowymi warunkami wstępnymi we wszystkich 100 miejscach, które je zastępują.Jest to coś, czego chciałbym, aby każdy język miał, a także życzyłem sobie, nawet w C ++, aby była to trochę bardziej natywna koncepcja (np. Nie wymagająca od nas definiowania oddzielnej funkcji do zastąpienia).
Jest to niezwykle przydatne, jeśli nie zrobiłeś tego
assert
wcześniej, i zdałeś sobie sprawę, że potrzebujesz go później, gdy niektóre losowe miejsca w bazie kodu napotykały ujemne wartościf
.źródło
Po pierwsze: wiele kopii klasy podstawowej (problem C ++) i ścisłe powiązanie między klasami podstawowymi i pochodnymi.
Po drugie: wielokrotne dziedziczenie po abstrakcyjnych interfejsach
źródło