Czytałem o wzorach projektowych i czytałem, że prototypowy wzór projektowy eliminuje nadmierną podklasę.
Dlaczego podklasowanie jest złe? Jaką przewagę przyniosłoby zastosowanie prototypu nad podklasą?
design-patterns
inheritance
David Faux
źródło
źródło
Odpowiedzi:
Dlaczego podklasowanie jest zbyt złe?
„Zbyt dużo” to wezwanie do sądu; ale weź to ode mnie, to jest złe. Kod, z którym pracuję, ma dziedziczenie DEEP, a kod jest cholernie trudny do odczytania, zrozumienia, śledzenia, śledzenia, debugowania itp. Itd. Pisanie kodu testowego dla tych rzeczy jest praktycznie niemożliwe.
Wzór prototypowy eliminuje podklasowanie
Czy pytanie to oznacza „eliminuje zbyt wiele podklas?”. Ten wzorzec wymaga klonowania obiektu „szablonu”, aby uniknąć podklasowania, przynajmniej w tym momencie. Nie ma reguły, która mówi, że „szablon” nie może być podklasą.
Preferuj kompozycję zamiast dziedziczenia
Pomysł ten obejmuje również delegowanie i agregację. Zgodnie z tą heurystyką oznacza to, że twoje oprogramowanie jest bardziej elastyczne, łatwiejsze w utrzymaniu, rozszerzaniu i ponownym użyciu.
Gdy klasa składa się z części , można je zastąpić w czasie wykonywania . Ma to ogromny wpływ na testowalność.
Testowanie jest łatwiejsze . Możesz użyć fałszywych części (tj. „Kpiny”, „dublowanie” i inne rozmowy testowe). Głębokie dziedziczenie naszego kodu oznacza, że musimy utworzyć instancję całej hierarchii, aby ją przetestować. W naszym przypadku nie jest to możliwe bez uruchomienia kodu w prawdziwym środowisku. Na przykład potrzebujemy bazy danych w celu tworzenia instancji obiektów biznesowych.
Zmiany wiążą się z efektami ubocznymi i niepewnością - im bardziej „bazowa” klasa, tym bardziej rozpowszechnione efekty, na dobre i na złe. Mogą wystąpić pożądane zmiany, których nie odważysz się wprowadzić z powodu niepewności skutków ubocznych. Lub zmiana, która jest korzystna dla jednego miejsca w naszym łańcuchu spadkowym, jest niekorzystna dla innego. To z pewnością moje doświadczenie.
źródło
Myślę, że jednym z powodów, dla których uznano to za złą praktykę, jest to, że z biegiem czasu każda podklasa może zawierać atrybuty i metody, które nie mają sensu dla tego obiektu, im dalej schodzisz w dół łańcucha (w słabo rozwiniętej hierarchii). Możesz skończyć z rozdętą klasą, która zawiera przedmioty, które nie mają sensu dla tej klasy.
Jestem do tego agnostyczny. Dogmatyczne wydaje się stwierdzenie, że jest złe. Wszystko jest złe, gdy jest niewłaściwie używane.
źródło
Dwa powody.
To wydajność środowiska wykonawczego. Za każdym razem, gdy wywołujesz podklasę z nadklasy, wykonujesz wywołanie metody, więc jeśli zagnieździłeś pięć klas i wywołujesz metodę w klasie podstawowej, będziesz wykonywać pięć wywołań metod. Większość kompilatorów / środowisk wykonawczych spróbuje to rozpoznać i zoptymalizować dodatkowe połączenia, ale jest to bezpieczne tylko w bardzo prostych przypadkach.
To zdrowy rozsądek waszych kolegów programistów. Jeśli zagnieżdżycie pięć klas, muszę zbadać wszystkie cztery klasy nadrzędne, aby ustalić, czy naprawdę wywołujecie metodę w klasie podstawowej, staje się to żmudne. Podczas debugowania programu może być również konieczne wykonanie pięciu wywołań metod.
źródło
„Zbyt dużo” to wezwanie do osądu.
Podobnie jak w przypadku większości rzeczy w programowaniu, podklasowanie nie jest z natury złe, gdy ma sens. Gdy model rozwiązania problemu ma bardziej sens jako hierarchia niż kompozycja części, podklasowanie może być preferowaną opcją.
To powiedziawszy, preferowanie kompozycji zamiast dziedziczenia jest dobrą regułą.
Gdy podklasujesz , testowanie może być trudniejsze (jak zauważył radarbob ). W głębokiej hierarchii izolowanie problematycznego kodu jest trudniejsze, ponieważ utworzenie instancji podklasy wymaga wprowadzenia całej hierarchii nadklasy i szeregu dodatkowego kodu. Dzięki podejściu kompozycyjnemu możesz izolować i testować każdy komponent obiektu agregującego za pomocą testów jednostkowych, obiektów próbnych itp.
Podklasowanie może również spowodować ujawnienie szczegółów implementacji, których możesz nie chcieć. Utrudnia to kontrolę dostępu do podstawowej reprezentacji danych i wiąże cię z konkretną implementacją modelu tworzenia kopii zapasowych.
Jednym z przykładów, o którym mogę pomyśleć, jest java.util.Properties, który dziedziczy z Hashtable. Klasa Properties przeznaczona jest wyłącznie do przechowywania par String-String (jest to odnotowane w Javadocs). Z powodu dziedziczenia metody put i putAll z Hashtable zostały udostępnione użytkownikom właściwości. Chociaż „właściwy” sposób przechowywania właściwości to setProperty (), absolutnie nic nie stoi na przeszkodzie, aby użytkownik wywołał metodę put () i przekazał ciąg znaków i obiekt.
Zasadniczo semantyka właściwości jest źle zdefiniowana z powodu dziedziczenia. Ponadto, jeśli ktokolwiek zechce kiedykolwiek zmienić obiekt kopii zapasowej właściwości, wpływ będzie miał znacznie większy wpływ na kod korzystający z właściwości, niż gdyby właściwości przyjęły bardziej kompozycyjne podejście.
źródło
Dziedziczenie może w niektórych przypadkach przerwać enkapsulację , a jeśli tak się stanie, jest to złe. Czytaj więcej.
W dawnych dobrych czasach bez dziedziczenia biblioteka publikowałaby interfejs API, a aplikacja korzystająca z niego korzysta z tego, co jest publicznie widoczne, a biblioteka ma teraz swój własny sposób obsługi wewnętrznych narzędzi, które ciągle się zmieniają bez przerywania aplikacji.
W miarę ewolucji potrzeb biblioteka może ewoluować i może mieć więcej klientów; lub może zapewnić rozszerzoną funkcjonalność, dziedzicząc po swojej klasie z innymi smakami. Na razie w porządku.
Jednak podstawową rzeczą jest to, że jeśli aplikacja podejmie klasę i zacznie ją podklasować, teraz podklasa jest właściwie klientem, a nie partnerem wewnętrznym. Jednak w przeciwieństwie do wyraźnego, jednoznacznego sposobu definiowania publicznego API, podklasa jest teraz znacznie głębsza w bibliotece (dostęp do wszystkich prywatnych zmiennych i tak dalej). Nie jest jeszcze źle, ale paskudna postać może zmostkować interfejs API, który jest zbyt duży w aplikacji, a także w bibliotece
W istocie, chociaż nie zawsze tak może być, ale:
Zezwolenie na podklasę może być błędne, jeśli w tym momencie.
źródło
Krótka szybka odpowiedź:
To zależy od tego, co robisz.
Rozszerzona nudna odpowiedź:
Deweloper może stworzyć aplikację. przy użyciu szablonów podklas lub prototypowych. Czasami jedna technika działa lepiej. Czasami inna technika działa lepiej.
Wybór za pomocą techniki działa najlepiej, wymaga również rozważenia, czy obecny jest scenariusz „Wielokrotnego dziedziczenia”. Nawet jeśli język programowania obsługuje tylko pojedyncze dziedziczenie, szablony lub prototypy.
Uwaga uzupełniająca:
Niektóre odpowiedzi dotyczą „wielokrotnego dziedziczenia”. Myślenie, a nie główne pytanie, jest związane z tematem. Istnieje kilka podobnych postów na temat „wielokrotnego dziedziczenia a składu”. Miałem przypadek, w którym mieszałem oba.
źródło