Cześć! Dziś czas poruszyć kolejny temat, którego dotychczas brakowało w Cesarstwie-Dev – temat wzorców projektowych. Pierwszy z tej serii wpis będzie zawierał wprowadzenie do wzorców, jak i opis mojego ulubionego wzorca o wspaniałej nazwie Dekorator. Kolejne wpisy z tej serii będą najpewniej zawierały dwa wzorce. Jednak nie ma na co czekać, w końcu przed nami dużo wartościowego materiału. Zapraszam!

Photo by jose luis navarro from FreeImages

Wstęp

Wzorce projektowe. Każdy programista nieraz słyszał ten zlepek słów w formie polskiej, jak i angielskiej (Design Patterns). Przytoczmy pewną definicję w celu rozpoczęcia rozważań: Wzorce projektowe to uniwersalny sposób na rozwiązanie powtarzalnego problemu. Definicja krótka, lecz treściwa! Ukazuje ona wiele cech wzorców projektowych. Za chwile przybliżymy niektóre z nich, lecz zacznijmy od historii, gdyż nie jest ona banalna. Wzorce projektowe zostały przez nas, programistów, wyłącznie zapożyczone od architekta Christophera Alexandra. Twierdził on, że w architekturze istnieje wiele generycznych problemów, które rozwiązywane są indywidualnie przez każdego architekta. W celu rozwiązania tego problemu napisał książkę, która miała opisywać wzorcowe podejście do projektowania miast czy budowli.

Jego podejście zainspirowało wiele wspaniałych umysłów, dzięki czemu już w latach 60 było widać powielanie jego pomysłów przy tworzeniu oprogramowania. Największy wpływ na świat programistów miała jednak wydana w latach 90 słynna książka „Bandy czterech”. Design Patterns: Elements of Reusable Object-Oriented Software  jest to książka, która zapoczątkowała istną rewolucję wzorcową. Niewątpliwie jest to jedna z najważniejszych książek świata IT. Autorzy opisują w niej możliwości (jak i pułapki) programowania obiektowego oraz przedstawiają 23 wzorce projektowe.

Wzorce projektowe

W naszym świecie wzorce projektowe są uniwersalnym sposobem na rozwiązanie powtarzalnego problemu. Czy to oznacza, że możemy po prostu wrzucić gotowy „wzorzec” do naszego kodu i on będzie automagicznie działał oraz spełniał wszystkie wymagania czystego kodu? Oczywiście odpowiedź musi być przecząca. Wzorce są wyłącznie wytycznymi, według których powinniśmy postępować. Zdecydowanie nie jest to zbiór ściśle określonych zasad i zakazów. Każdy wzorzec musimy odpowiednio dostosować przed wrzuceniem do naszego projektu. W takim razie, jaka korzyść płynie z ich korzystania? Korzyści jest wiele!

Zacznijmy od używania słownictwa znanego przez szerokie grono programistów. Dużo łatwiej nam się dogadać ze sobą, gdy nie musimy tłumaczyć poszczególnych klas czy rozwiązań. Jeśli powiemy programiście, że ta część kodu to zwykła zdalna fasada, bądź fabryka strategii, to od razu będzie wiedział, za co dany podzbiór klas odpowiada. Oczywiście wyłącznie pod względem technicznym, gdyż w zasady domenowe będzie musiał się wdrożyć niezależnie.

Trzeba również pamiętać, że wzorce projektowe powstały dawno temu. Na przykład wzorzec dekorator przedstawiła nam Banda Czterech w swojej książce, więc zdecydowanie jest już pełnoletni! Wzorce są używane w niezliczonych projektach programistycznych i…. po prostu działają. Wzorce są sprawdzonymi oraz wyspecjalizowanymi rozwiązaniami, które spełniają konkretne potrzeby techniczne. Warto zwrócić uwagę, że wzorce powstały w celu rozwiązania zadanego problemu. Nie działają one jak magiczny eliksir, który uleczy cały kod. Przed użyciem konkretnego wzorca należy się zastanowić, czy na pewno spełnia on wymagania, które nasz kod musi spełnić. Nie można popadać w wzorcomanię, i w każde miejsce wrzucać jakiś wzorzec projektowy. Wybranie odpowiedniego wzorca wymaga ich znajomości oraz doświadczenia.

Dekorator – teoria

Mam nadzieję, że po dzisiejszym wpisie będziecie wiedzieli kiedy warto użyć wzorca Dekorator. W pierwszej części jego opisu skupimy się na teorii związanej z tym wzorcem. Natomiast w drugiej części pokażemy przykładową implementację (nie będzie ona oparta na kawie z mlekiem, obiecuję!). Na konferencjach często słyszymy o zasadach SOLID, których powinien przestrzegać każdy programista, chcący pisać czysty kod. Tak przynajmniej słyszałem na konferencji…. Niezależnie, wzorzec dekorator pozwala nam dostosować się do literki O z tego akronimu. Odpowiada ona za zasadę open-closed principal – kod powinien być otwarty na rozszerzanie i zamknięty na modyfikację. Używanie tego wzorca pozwala na dodawanie nowych zachowań (otwarty na rozszerzanie) bez zmiany istniejącej implementacji (zamknięty na modyfikację). W książce Rusz Głową! Wzorce projektowe tak jest definiowany tez wzorzec:

Dekorator – pozwala na dynamiczne przydzielenie danemu obiektowi nowych zachowań. Dekoratory dają elastyczność, podobną do tej, jaką daje dziedziczenie, oferując jednak w zamian znacznie rozszerzoną funkcjonalność

Definicja jest na tyle prosta, że możemy przejść do kolejnej części opisu każdego wzorca projektowego – diagramu.

Decorator pattern - Wikipedia

Na górze diagramu widzimy pewien abstrakcyjny komponent. Jak widać, odpowiada on za pewną operację. Z lewej strony widzimy konkretną, istniejącą implementację takiego komponentu. Z prawej strony definiujemy abstrakcyjny dekorator, który zawiera referencję do dowolnej implementacji wyższego komponentu. Warto zwrócić uwagę, że dekorator ten współdzieli interfejs z komponentem górnym, co oznacza, że jest nieodróżnialny przez konsumentów zewnętrznych. Na samym dole pokazana jest implementacja dekoratora. Przedstawmy jeszcze ogólne zalety tego wzorca:

  • Zapewnia większą elastyczność niż dziedziczenie. Pozwala na dodawanie i usuwanie zachowań w czasie wykonywania programu
  • Pozwala wydzielić funkcjonalności do wielu klas, dzięki czemu model na wysokim poziomie w hierarchii może skupić się na swoim głównym zadaniu, zamiast dostosowywać się do wszystkich możliwych funkcji pobocznych

Dekorator – praktyka

Znamy już teorię dotyczącą dekoratora, czas więc zapoznać się z przykładowym jego użyciem! Wpierw chcę jednak zauważyć, że będzie ono pomijało abstrakcyjną klasę dekoratora. Jest to całkowicie akceptowalne w przypadku implementacji wyłącznie jednej dodatkowej funkcjonalności. Przejdźmy do definicji problemu. Aby dodać dramaturgii, załóżmy, że pracujemy przy starym projekcie od lat działającym na produkcji. Dostaliśmy zadanie polegające na optymalizacji czasowej komunikacji z zewnętrznym systemem poprzez dodanie krótkotrwałego cache’a w pamięci. Znając wysoki stopień entropii naszego systemu (im wyższy stopień entropii, tym większa szansa, że coś się zepsuje przy zmianach) chcemy uniknąć modyfikacji istniejącej klasy, jak i nie zmieniać serwisów wykorzystujących powolny moduł. Na nasze szczęście nie musimy ekstraktować interfejsu tej klasy, gdyż ta już swój interfejs implementuje. Przyjrzyjmy się mu wraz ze sztuczną jego implementacją w celach pokazowych.

Według interesariuszy możemy pozwolić sobie na 30-sekundowe opóźnienie w sprawie aktualnego wyniku meczu, jeśli dzięki temu odciążymy serwis z wynikami oraz zapewnimy krótszy czas odpowiedzi. W tym celu napiszmy odpowiedni dekorator (pomijając abstrakcję nad nim, jak wspomniałem na początku tego akapitu).

Powyższy dekorator do funkcjonalności klienta dodaje przechowywanie w pamięci wyników na określony czas. Spójrzmy na przykładowe użycie.

Jak widać, używanie podstawowej wersji zmienia wynik natychmiastowo, natomiast wykorzystanie udekorowanego serwisu odświeża wynik meczu w 30-sekundowych interwałach.

Podsumowanie

Wzorce projektowe są potężnym narzędziem w arsenale doświadczonego programisty. Po tym wpisie do własnego narzędnika można dorzucić Dekorator! Trzeba jednak zauważyć, że nie jest łatwym po prostu wyszukać wzorzec pasujący do konkretnego problemu. W Google najczęściej trafimy na linki do popularnego serwisu, gdzie zamieszczone są głównie wyspecjalizowane rozwiązania internatów. Rzadko spotkamy się tutaj ze wzorcami projektowymi. Każdy programista musi przejść przez etap poznawania licznych wzorców – praktycznie i teoretycznie – aby móc sprawnie dopasowywać rozwiązanie do problemu. Warto więc poświęcić swój czas na rozwój w tym kierunku. Na pewno zapraszam do Cesarstwa-Dev, gdzie w przyszłości będą objaśniane kolejne wzorce projektowe!

Podsumujmy jeszcze krótko poznany dziś wzorzec. Dekorator pozwala na dynamiczne dodawanie funkcjonalności do obiektu, dzięki czemu możemy rozszerzać zachowania klas bez zmian ich implementacji. Jest to zdecydowanie wspaniałe narzędzie, które użyte w odpowiedniej sytuacji daje wiele korzyści programiście.

To już koniec dzisiejszego wpisu! Mam nadzieję, że Wam się podobało. Dajcie znać w komentarzach co myślicie na temat tego wpisu jak i samego wzorca Dekorator. Korzystacie z niego? Swoją drogą, wspomnieliśmy dzisiaj o integracji z zewnętrznym systemem. Jeśli chcesz przeczytać o procesie integracji z API Narodowego Banku Polskiego za pomocą testów, to zapraszam tutaj. Niezależnie, bardzo Ci dziękuje za dotrwanie do końca i gorąco zachęcam do dodania komentarza bądź do zapisania się na subskrypcję. Dzięki niej nie ominie Cię żaden wpis! A teraz nie pozostaje mi nic innego jak życzyć miłego dnia. Do napisania!


3 Komentarze

Arek · 2020-11-09 o 10:38

Cześć,
Nie wiem do jakiego odbiorcy kierujesz ten post ale dla początkujących programistów lub może nie całkiem początkujących ale takich którzy zaczynają przygodę z wzorcami projektowymi może być trudno zrozumieć ten wzorzec.

Brakuje mi tu porównania do wersji bez wzorca. Teoria jest wyjaśniona bardzo abstrakcyjnie (np. przydałoby się wskazać w implementacji co odpowiada elementom z diagramu).

Przykład jak najbardziej na plus dla mnie :).

Super by było jak byś wskazał jakie błędy się popełnia w wykorzystaniu tego wzorca. Może byłbyś wstanie wskazać potencjalny problem dla którego aż chciało by się zastosować ten wzorzec ale ostatecznie nie powinno (tak żeby pokazać że czasem wydaje się że do danego problemu nadaje się ten wzorzec ale mimo wszystko np. przez dalsze wymagania okazuje się że jednak nie będzie to dobry wzorzec do tego problemu – tak żeby to pokazać „Każdy wzorzec musimy odpowiednio dostosować przed wrzuceniem do naszego projektu”).

Takie tylko tipy z mojej strony.

    Cesarz · 2020-11-09 o 10:54

    Cześć! Dzięki za bardzo konstruktywny komentarz! Przy tworzeniu kolejnych wpisów z tej serii zdecydowanie wezmę Twoje rady pod uwagę, może nawet doprecyzuję samego Dekoratora – rzeczywiście pominąłem jego wady i pułapki. Myślę, że może powstać z tego krótki i ciekawy wpis! Jeszcze raz dziękuję i pozdrawiam!
    Cieszę się, że przykład się podobał!

dotnetomaniak.pl · 2020-11-08 o 18:32

Wzorce projektowe – dekorator

Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *