2011-02-09 7 views
6

Ostatnio dyskutowaliśmy wadę mentioned here i jedna ze stron mówi coś w stylu „tak, to dlaczego książki mówią, że spadków należy unikać.Skąd pomysł na "dziedzictwo, którego należy unikać"?

używam dziedziczenie lat i uważają, że jest bardzo przydatny i wygodny Jestem także pewien, że osoba argumentująca co najmniej źle zrozumiała, co mówią te książki. "

Czy naprawdę istnieje pomysł, że dziedziczenie jest złe i czy należy go unikać? i gdzie mogę się więcej dowiedzieć?

+0

Cóż, nie wiem, ale wolę się od spadków. –

+0

@ Tuzo: Czy wszystko sprowadza się do "składu versus dziedziczenia"? – sharptooth

+0

Myślę, że to przynajmniej duża część (wszystkie odpowiedzi o tym wspominają). Chociaż muszę przyznać, że intelektualnie uwielbiam koncepcyjną elegancję dziedziczenia. ("Dlaczego się nie ożenię !?") :)) –

Odpowiedz

9

Myślę, że on może mieć chodzi o to, że dziedziczenie może być nadużywane, nawet w przypadkach, gdy kompozycja byłaby lepszym rozwiązaniem. Jest to omawiane w kilku książkach, np.

Dziedziczenie jest skutecznym sposobem, aby osiągnąć ponowne wykorzystanie kodu, ale to nie zawsze jest najlepszym narzędzie do pracy. Używany niewłaściwie, prowadzi do kruchego oprogramowania. Bezpieczne jest używanie dziedziczenia w pakiecie, w którym podklasy i implementacje nadklasy są pod kontrolą tych samych programistów. Bezpieczne jest również używanie dziedziczenia podczas rozszerzania klas specjalnie zaprojektowanych i udokumentowanych dla rozszerzenia (pozycja 17). Jednak dziedziczenie ze zwykłych klas konkretnych przez granice pakietów, , jest niebezpieczne. Przypomnijmy, że ta książka używa słowa "dziedziczenie" do średniego dziedziczenia implementacji (gdy jedna klasa rozszerza drugą). Problemy omówione w tym punkcie nie dotyczą dziedziczenia interfejsu (gdy klasa implementuje interfejs lub gdy jeden interfejs rozszerza się o inny).

W przeciwieństwie do inwokacji metody dziedziczenie narusza enkapsulację [Snyder86]. Innymi słowy, podklasa zależy od szczegółów implementacji jej nadklasy dla jej prawidłowego działania. Implementacja nadklasy może zmienić się z wydania , aby zwolnić, a jeśli tak, to podklasa może się zepsuć, mimo że jej kod nie został dotknięty . W konsekwencji podklasa musi ewoluować w parze z jej nadklasą, , chyba że autorzy klasy nadrzędnej zaprojektowali i udokumentowali ją specjalnie w celu jej przedłużenia.

  • Effective C++ 3rd Edition ma kilka elementów z tym związanych:
    • Item 32 "Jest-a" Upewnij modele dziedziczenia publicznego
    • Pozycja 34: Rozróżniać dziedziczenie interfejsu i dziedziczenia realizacji
    • Pozycja 38: model "ma-A" lub "jest realizowany w-warunkach-of" do składu
+0

Zobacz także "Head First Design Patterns". –

3

Nie należy go unikać za każdym razem, ale utrudnia to elastyczność wszędzie tam, gdzie utknąłeś ze zdefiniowanym zestawem operacji z nadklasy.Podstawową ideą projektu jest "pozostawienie do dziedziczenia części stałych i wstrzyknięcie części podlegających zmianie".

W Head First Desing Patterns jest dobry przykład.

Załóżmy, że zdecydowałeś się modelować kaczki z klasą bazową, która implementuje fly(). Powiedzmy, że masz gumokręta, który oczywiście nie może latać. Przesłonisz metodę fly() za pomocą metody, która nic nie robi. Ale pewnego dnia dodasz DecoyDuck i zapomnisz przesłonić fly(), a dostaniesz bałagan. Byłoby lepiej zbudować DecoyDuck wstrzykując pożądane zachowanie (kompozycję ponad dziedziczenie).

+0

Czy naprawdę można powiedzieć, że gumowa kaczka to kaczka, skoro nie lata jak kaczka? jeśli możesz, to fly()) naprawdę nadaje się do włączenia w klasie bazowej, ponieważ nie jest udostępniany przez wszystkich, które jest kaczka ... – Jean

+1

dokładnie; dziedziczenie nie nadaje się do rozwiązania tego rodzaju problemu: jeśli powiesz "tak, gumowa kaczuszka jest kaczką", musisz zastąpić muchę() w każdej podklasie; jeśli powiesz "nie", musisz go skopiować wszędzie. –

+0

Zgadzam się z tobą w zasadzie, ale uważam, że przykład jest mniej przekonujący. Myślę, że problem polega na tym, że abstrakcja RubberDuck nie jest poprawna. Jeśli spojrzysz na kaczkę i gumową kaczkę i spróbujesz je sklasyfikować, większość ludzi powie, że kaczka to zwierzę (lub ptak), podczas gdy gumowa kaczuszka to * zabawka *, która wygląda jak kaczka. –

2

Nie należy unikać dziedziczenia "tak jak jest".
Dziedziczenie powinno być używane (i używane) tylko wtedy, gdy chcesz zdefiniować/opisać relację "jest".

Kiedy jednostka "nie jest" aotherentity, podmiot nie powinien dziedziczyć z założenia. W takich przypadkach należy użyć kompozycji.

2

Dziedziczenie pomaga uogólniać (co jest upcasting w terminach OOP), podczas gdy skład pomaga w definiowaniu zachowania (Inwersja kontroli - lub Injection Dependency Injection).

Należy używać tego, który przynosi więcej korzyści; ale faworyzowanie kompozycji sprawia, że ​​kod jest bardziej rozszerzalny. Zatem kod staje się "zamknięty dla modyfikacji, ale otwarty na rozszerzenie", a zatem "HAS-A jest lepszy niż IS-A".

Trochę jak bycie samochód samodzielnie lub posiadanie samochodu (lub nie?)