[Wzorce projektowe] Wzorzec projektowy Dekorator

Cześć. Dziś post bardziej techniczny. No i pierwszy z serii, nieregularnej zresztą. Będę opisywał wzorce projektowe oraz ich przykładowe zastosowanie w praktyce. Na pierwszy ogień idzie wzorzec projektowy dekorator bądź z angielska decorator. Nie przedłużając dłużej, zaczynamy!

Jest to pierwszy post z serii, więc zacznę od podstawowego pytania:

Co to jest wzorzec projektowy?

Wyobraź sobie, że wiele problemów, które napotykasz tworząc swoje własne oprogramowanie, jest powtarzalna, spotyka nie tylko ciebie, ale też została już rozwiązana i ładnie opisana. Tym właśnie jest wzorzec projektowy – praktycznie gotowym rozwiązaniem na twój problem. Warto zaznaczyć, że wzorzec jest opisem, implementacja należy do ciebie jako programisty. Wzorców projektowych mamy kilka rodzajów, a taki podstawowy podział to:

  • Wzorce kreacyjne – takie, które opisują tworzenie obiektów
  • Strukturalne – takie, które opisują struktury obiektów
  • Behawioralne – takie, które opisują zachowanie obiektów i relacje między sobą

Wzorzec projektowy dekorator – teoria

Dekorator jest strukturalnym wzorcem i chyba jednym z najprostszych. Co rozwiązuje ten wzorzec? Wyobraź sobie klasę, której brakuje jakiejś funkcjonalności. Za pomocą tego wzorca możemy rozszerzyć funkcjonalność klasy o jakąś nową funkcjonalność.  Łatwo zapamiętać – dekorujemy klasę nową funkcjonalnością. Wzorzec projektowy dekorator jest opakowaniem na istniejącą już klasę, a co więcej, może być opakowaniem na opakowanie!

Wzorzec projektowy dekorator – praktyka

Przygotowanie

Wiemy w teorii, do czego służy wzorzec projektowy dekorator i być może świeci ci się już lampka jak by to można zastosować. Jeśli jeszcze nie wiesz, to już za chwilę się dowiesz. Zacznijmy od sytuacji – mamy serwis, który wypluwa (ok, renderuje) nam stringa jako dane. Chcielibyśmy móc renderować je np do JSON’a albo XML’a bądź jakiegokolwiek innego. Zacznijmy od interfejsu dającego metodę renderowania:

Nic nadzwyczajnego, ale przyda nam się potem.

Dobra, teraz stwórzmy klasę konkretnego serwisu:

Jak widzimy klasa jest banalnie prosta, pominąłem już jakiekolwiek konstruktory itp, bo nie o to chodzi. Mamy chronione pole (może być prywatne) $data, które jest stringiem zwracanym metodą getData();

Wzorzec strukturalny dekorator w akcji

Naszym dekoratorem będzie klasa abstrakcyjna, bo tak wygodnie. Oczywiście implementacja tego wzorca może być różna, ja wybrałem taką, potem pokażę jeszcze inną. W każdym razie tworzymy klasę abstrakcyjną, która będzie implementowała ten sam interfejs co nasz serwis:

Okej, co tu się dzieje? Mamy abstrakcyjną klasę, dzięki której nie będziemy duplikować kodu, gdy będziemy chcieli stworzyć więcej dekoratorów. W konstruktorze przyjmuje obiekt implementujący wcześniej stworzony przez nas Interfejs.

Teraz stwórzmy już konkretny dekorator:

Myślę, że tu nie ma wiele do tłumaczenia. Konkretny dekorator jest rozszerzeniem klasy RendererDecorator i na swój sposób renderuje dane, w tym wypadku jest to JSON. Zauważ, że wszędzie jest ta sama metoda – render. A wszystkie klasy implementują RenderableInterface. To bardzo ważne, bo:

Co by nie było pod zmienną $service, mamy metodę render. Końcowego użytkownika będzie właśnie to obchodziło.

Podsumowanie

Jak pewnie zauważyłeś, ten wzorzec projektowy jest bardzo prosty i nie powinien przysporzyć problemu, jego implementacja może być naprawdę różna, a to powyżej jest tylko jeden z przykładów. Pomoże nam rozbudować istniejące klasy w sposób dynamiczny.

Znałeś ten wzorzec wcześniej? Kiedy go stosujesz? Podziel się w komentarzu!