Dekorator (decorator)

Posucha na blogu, czas ją przerwać. Jedyna kategoria, do której nie dodałem jeszcze opisu wzorca to kategoria wzorców strukturalnych (dla przypomnienia, opisujących powiązania między obiektami). Uzupełniając braki zajmijmy się jej reprezentantem, niech będzie to wzorzec dekorator.

Dekorator

Sama jego nazwa sugeruję nam mocno, że będziemy coś dekorować, czyli wzbogacać. Jego zadanie to rozszerzenie funkcjonalności jakiejś klasy o nowe metody, pola. Można porównać jego działania do trywialnego dziedziczenia, klasa po klasie, z tą różnicą, że wzorzec dekorator rozszerza funkcjonalność klasy w trakcie działania programu, a używając dziedziczenia, wszystkie funkcjonalności musimy zdefiniować przed kompilacją programu.

Dzieje się to poprzez utworzenie nowej klasy, która dziedziczy po tym samym interfejsie, co klasa dekorowana. Z pomocą konstruktora przyjmuje ona obiekt konkretnej klasy, poprzez niego wywołuje metody „starej” klasy oraz w zależności od potrzeb nowej klasy, sama ma zdefiniowane konkretne funkcjonalności. Koniec teorii przejdźmy do konkretów.

Diagram klas

Wzorzec projektowy dekorator

Wzorzec projektowy dekorator

Kod źródłowy

Zasymulujmy sytuację, kiedy mamy klasę pełniąca funkcję kalkulatora prostego, zawierającą cztery podstawowe działania matematyczne, z której poprzez dekorację, będziemy chcieli utworzyć kalkulator 2.0, nazwijmy go dla jasności naukowym.

Zacznijmy od stworzenia interfejsu dla kalkulatora IKalkulator, umieśmy go w pliku IKalkulator.cs.

Zawiera on cztery podstawowe działania matematyczne, pole przechowujące rodzaj kalkulatora oraz metodę wyświetlającą ten rodzaj.

Następnie utwórzmy klasę, dla kalkulatora prostego KalkulatorProsty.cs.

Utworzyliśmy konkretną klasę, która dziedziczy po naszym interfejsie. Musimy przez to w niej zaimplementować wymagane funkcje. W tej chwili jest ona w pełni funkcjonalna, po utworzeniu dla niej obiektu, moglibyśmy korzystać z jej funkcji. W dalszej części artykułu będziemy dążyć do rozbudowania jej funkcjonalności.

Utwórzmy teraz klasę dekoratora, która zapewni nam możliwość korzystania z funkcji klasy, którą rozszerzamy, Dekorator.cs.

Jak widzimy, zagwarantowanie dostępu do metod z rozszerzanej klasy, zostało zapewnione poprzez wywołanie ich na instancji obiektu, który został przekazany przez parametr konstruktora. Referencja do przekazanego obiektu zapamiętywana jest w odpowiednim polu, dla uniwersalności obiekt konkretnej klasy „boxowany” jest do interfejsu, dzięki temu nie zawężamy sobie możliwości klasy Dekorator, tylko do klasy KalkulatorProsty.

Pozostało nam stworzyć instancję klasy, która rozszerzy możliwości kalkulatora prostego, KalkulatorNaukowy.cs.

Przyjmuje ona w konstruktorze obiekt kalkulatora prostego. Argument konstruktora zostaje przekazany do klasy dekoratora, która gwarantuje, że metody kalkulatora prostego będą działać. Sama klasa kalkulatora naukowego rozszerza funkcjonalność kalkulatora prostego o jedną nową funkcjonalność, mianowicie pierwiastkowanie, bardzo trywialny przykład, ale skupiamy się na zasadach panujących we wzorcu, a nie tworzeniu rozbudowanych funkcjonalności.

Na koniec główna klasa i metoda wejściowa programu dla zaprezentowania efektów.

Wyjście:

Jak napisałem wcześniej kalkulator prosty jest w pełni funkcjonalną klasą, można korzystać ze zdefiniowanych w nim metod. Kalkulator naukowy tworzony jest na bazie kalkulatora prostego, zyskujemy nową funkcjonalność, nie tracąc przy tym już istniejących.

Podsumowanie

Wspólnie poznaliśmy wzorzec, który pozwala rozszerzyć funkcjonalności klasy bez modyfikacji jej kodu. Jest alternatywą dla zwykłego dziedziczenia, ale przeznaczony przede wszystkim do wzbogacania (dekorowania) klas w działających programach. Mam nadzieję, że jego implementacja nikomu nie sprawi już trudności.

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

Dodaj komentarz