Dziedziczenie vs kompozycja
Kiedy blisko 10 lat temu zacząłem trochę bardziej serio programować obiektowo, byłem ?zauroczony? trzema paradygmatami stojącymi za tym podejściem.
Dla przypomnienia są to (za Wikipedią):
- abstrakcja,
- hermetyzacja,
- polimorfizm.
Każdy z nich jest ważny i w pewnym sensie definiuje to, jak postrzegamy programowanie obiektowe. Z biegiem lat jednak dostrzegłem wady ostatniego paradygmatu. Dziedziczenie to diabelska pokusa, której trudno się oprzeć. Czasem próbujemy używać go w takich miejscach, w których można by ze spokojem zastosować inne, bardziej eleganckie i satysfakcjonujące rozwiązanie. Do grona alternatyw z pewnością można zaliczyć podejście oparte na kompozycji, które zyskuje na znaczeniu zwłaszcza wtedy, gdy w projekcie używamy IoC.
Co złego jest w dziedziczeniu?
Jeśli tworzymy rozwiązanie wykraczające poza pojedynczy projekt (tworzymy własne SDK, framework), to istnieje spore prawdopodobieństwo, że dziedziczenie wówczas się nie sprawdzi. W pewnym momencie dojdzie do takiej sytuacji, że potencjalne wdrożenie będzie wymagało zmian w SDK, co może spowodować konflikty w innych korzystających z niego projektach. Równie prawdopodobne jest, że odziedziczymy klasę, w której będziemy modyfikować, lub też nadpisywać, część z wcześniej utworzonych metod. Takie działanie nie jest mile widziane i może w łatwy sposób doprowadzić do złamania niektórych punktów z mnemonika SOLID. Wykorzystując rozbudowane dziedziczenie, generujemy sporo innych problemów:
- Znacząco utrudniamy tworzenie testów jednostkowych lub całkowicie uniemożliwiamy ich napisanie.
- Utrudniamy sobie życie przy debugowaniu, w sytuacji gdy metody i właściwości naszego obiektu znajdują się na różnych poziomach dziedziczenia.
- Łamiemy postanowienia mnemonika SOLID.
- Tworzymy kod, którego zachowanie jest trudniejsze do przewidzenia.
- Niwelujemy możliwość wprowadzenia interfejsów, co powoduje, że stajemy się bardziej uzależnieni od konkretnej implementacji.
W czym kompozycja jest lepsza od dziedziczenia?
Pewną ciekawą alternatywą jest podejście oparte na kompozycji, które niweluje większość przytoczonych problemów. Stosując ją, możemy świetnie wpasować się w realia SOLID. Tworzymy w tym przypadku klasy, które często implementują konkretne interfejsy. Jeśli we wdrożeniu nie odpowiada nam określona implementacja generatora raportów, to po prostu tworzymy własną, która implementuje metody wcześniej utworzonego interfejsu i jednocześnie wykorzystuje inne, dostępne w naszym projekcie klasy.
W tym przypadku kluczem do sukcesu jest pilnowanie zasady pojedynczej odpowiedzialności poszczególnych klas. Łatwiej jest nam utworzyć pojedynczą nową implementację klasy rozwiązującej konkretny problem i zarejestrować ją w kontenerze IoC, niż głowić się nad tym, co autor klasy bazowej miał na myśli.
Czy powinniśmy zrezygnować z dziedziczenia? Czy jest dla niego jakieś sensowne zastosowanie?
Czy bazując na tym, co napisano powyżej, powinniśmy teraz zabrać swoje wszystkie zabawki z piaskownicy zwanej dziedziczenie i wynieść się do obozu zwolenników kompozycji? Cóż, to zależy od sytuacji. Jest wiele obszarów programowania, w których kompozycja sprawdza się lepiej, ponieważ takie podejście zwiększa dość mocno naszą elastyczność. Z drugiej strony są pewne problemy, w których rozwiązaniu lepiej sprawdzi się stare, poczciwe dziedziczenie. Jednym z nich jest problem drzewa obiektów.
Jeśli spojrzycie na popularne języki programowania wykorzystujące UI, to dostrzeżecie pewną prawidłowość. W większości z nich layout jest tworzony za pomocą drzewa obiektów, co strukturalnie przypomina język XML. Mamy element główny, a później kolejne zagnieżdżenia. Podobnie sytuacja wygląda z perspektywy klas. Mamy klasę główną typu View, Element, UIObject (itd.) oraz klasy potomne z niej dziedziczące.
Łatwo sobie wyobrazić np. taką hierarchię:
Element => Control => Button => RadioButton
Element => Control => Button => ImageButton itd.
W takich sytuacjach dziedziczenie sprawdza się świetnie, ponieważ kolejne elementy interfejsu zyskują istotne właściwości od swoich rodziców. Ponadto, przeszukując drzewo obiektów, możemy szukać kolejnych obiektów typu „Element”, a później dokonywać odpowiedniego rzutowania, na interesujący nas obiekt docelowy. Czy kompozycja sprawdziłaby się w tym miejscu? Być może, ale mnie bardziej pasuje tu dziedziczenie. W tym przypadku po prostu działa.
Szukasz informacji? Kliknij poniżej:
Zobacz nasze propozycje
-
- Druk
- PDF + ePub + Mobi
(71,40 zł najniższa cena z 30 dni)
71.40 zł
119.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
Czasowo niedostępna
-
- Druk
- PDF + ePub + Mobi
(59,40 zł najniższa cena z 30 dni)
59.40 zł
99.00 zł (-40%) -
- Videokurs
(69,65 zł najniższa cena z 30 dni)
129.35 zł
199.00 zł (-35%) -
- Druk
- PDF + ePub + Mobi
(44,85 zł najniższa cena z 30 dni)
41.40 zł
69.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
Niedostępna
-
- Druk
- PDF + ePub + Mobi
(53,40 zł najniższa cena z 30 dni)
53.40 zł
89.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(35,40 zł najniższa cena z 30 dni)
35.40 zł
59.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
Czasowo niedostępna
-
- Druk
- PDF + ePub + Mobi
(38,35 zł najniższa cena z 30 dni)
35.40 zł
59.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(25,92 zł najniższa cena z 30 dni)
23.94 zł
39.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(38,35 zł najniższa cena z 30 dni)
35.40 zł
59.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(29,18 zł najniższa cena z 30 dni)
26.94 zł
44.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(25,92 zł najniższa cena z 30 dni)
23.94 zł
39.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(51,35 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%) -
- Druk
(57,84 zł najniższa cena z 30 dni)
53.40 zł
89.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
Niedostępna
-
- Druk
- PDF + ePub + Mobi
- Audiobook MP3
(29,94 zł najniższa cena z 30 dni)
29.94 zł
49.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
- Audiobook MP3
Niedostępna
-
- Druk
- PDF + ePub + Mobi
(47,40 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%)