Kompozyt (Composite)

Posted on by 0 comment

Pod rozważania weźmiemy strukturalny wzorzec projektowy (strukturalne wzorce projektowe odpowiadają za regulację powiązań między klasami), którym jest Kompozyt (Composite). Warto, choć pobieżnie znać ten wzorzec, ponieważ jest dość często używany w programowaniu.

Kompozyt

Nazwa wzorca naturalnie nawiązuje do jego budowy, efektem zastosowania wzorca jest struktura drzewiasta obiektów np.

Wzorzec ten stosujemy, gdy grupę obiektów należących do pewnego typu, możemy w pewnych aspektach traktować tak jak pojedynczy obiekt. Ułatwia nam to manipulację dużą liczbą obiektów, zamiast odnosić się do nich pojedynczo, możemy wywołać akcję na całej grupie.

Diagram klas

Kompozyt (composite) wzorzec projektowy

Kompozyt (composite) wzorzec projektowy

 

Gdy mówimy o wzorcu kompozyt, możemy dokonać jego dysocjacji na poszczególne składowe:

  • Liść (Leaf) – reprezentuje prymitywny obiekt nie posiadający potomków,
  • Kompozyt (Composite) – reprezentuje grupę obiektów, składającą się z „liści”, implementuje akcje interfejsu Komponent,
  • Komponent (Component) –  interfejs, który implementują obiekty, definiuje ich domyślne zachowanie,
  • Klient – operuje na obiektach zawartych  w układzie.

Kod źródłowy

Zastosujmy ten wzorzec w gabinecie stomatologicznym 🙂 Załóżmy, że pani stomatolog, po zakończonej wizycie, chce wydrukować klientowi zestawienie wykonanych operacji oraz ich koszt.

Dostarczmy interfejs Komponent, który będzie narzucał podstawową implementację naszym klasom pochodnym, moglibyśmy tutaj użyć także klasy abstrakcyjnej, kwestia w wyborze należy do programisty, należy pamiętać, że klasa abstrakcyjna daje nam troszkę większą swobodę implementacji (może zawierać pola, konstruktory, ciała metod itp.) :

Widzimy tutaj trzy metody oraz dwie właściwości charakteryzujące naszą klasę.

W kolejnym kroku zaaranżujemy dwie klasy należace do grupy Component, jedna z nich będzie przechowywała operacje potrzebne do usunięcia ubytku na powłoce zęba (UsuniecieUbytku), druga będzie składowała wszystkie czynności wykonane podczas wizyty u pani dentystki (Wizyta):

Obie klasy implementują założenia zawarte w interfejsie . Właściwość Cena przechowuje koszt wykonania czynności w podzbiorze, we właściwości NazwaWyswietlana zawarta jest wyświetlana nazwa obiektu. Wartym uwagi elementem jest Lista _dzieci oraz metody na niej operujące, która przechowuje operacje składające się na tę czynność. Ostatnim składnikiem klasy jest funkcja Wyswietl() odpowiedzialna, za „wydrukowanie” zestawienia.

Kolejnymi składowymi są elementy „liście”, które są dla nas swego rodzaju operacjami atomowymi. Można by się zastanowić, czy nie utworzyć jednej klasy Lisc i poprzez properties NazwaWyswietlana definiować jej rodzaj, jednak uznałem, że przecież klasa Przeglad mogłaby posiadać dodatkowe funkcjonalności, których nie musimy definiować na potrzeby przykładu. Ponadto jest to dobry moment na ukazanie, że obiekty typu „Liść” nie muszą być jednego typu, a muszą być spójne pod względem określonego zachowania, po którym mamy korzyść z ich zgrupowania:

Jak widzimy w elementach „prostych” nie ma Listy przechowującej kolekcji obiektów, ponieważ pełnią one w naszym przykładzie rolę typów atomowych. Wynikiem tego są bezużyteczne metody Dodaj() oraz Usuń().

Pozostało nam sprawdzenie jak nasz projekt zachowuje się w praktyce:

Argumentami konstruktorów są nazwy wyświetlane metod.

Wynik naszych działań, który ukazał się na konsoli:

Podsumowanie

Reasumując kompozyt to wzorzec reprezentujący strukturalną grupę wzorców projektowych. Dzięki jego implementacji ułatwiamy pracę na zbiorze podobnych pod jakimś względem obiektów, nie tracąc jednocześnie dostępu do pojedynczych składowych, które stanowią grupę. Daje to wrażenie, że odwołujemy się do pojedynczego obiektu, gdy faktycznie działamy na rodzinie obiektów.

Mam nadzieję, że po przeczytaniu tego artykułu implementacja omawianego wzorca, będzie tylko przyjemnością.

Wpis należy do serii postów o wzorcach projektowych.

Dodaj komentarz