Wzorce projektowe – dekoratory

decoratorDekorator to jeden se strukturalnych wzorców projektowych, dzięki któremu możemy wykorzystać kompozycję w alternatywie do dziedziczenia w celu rozszerzenia zachowania klasy.
W przypadku dziedziczenia klasa rozszerzana jest w trakcie kompilacji w przeciwieństwie do dekoratorów, które rozszerzają klasy w trakcie działania programu.

 

 

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

Pamiętam jak zaczynałem się uczyć programowania i Javy, z przerażeniem oglądałem źródła przykładów np. do odczytów plików. Nie ja pierwszy i nie ostatni, a w sieci można wyszukać sporo zapytań typu: Drodzy programiści Javy – dlaczego Java tak boli? Otóż boli ponieważ źródłem bólu jest niewiedza.
Zobaczmy przykład strumieni:

To typowy przykład dekoratorów w Javie. Abstrakcyjną klasą bazową jest InputStream po której dziedziczą FileInputStream, BufferedInputStream i DataInputStream. Dekorowanie odbywa się w następujący sposób:

  1. W pierwszej kolejności tworzony jest obiekt FileInputStream z przekazanym obiektem pliku.
  2. Następnie klasa BufferedInputStream dekoruje FileInputStream rozszerzając ją o wewnętrzne buforowanie odczytywanych danych.
  3. Na samym końcu klasa DataInputStream dekoruje BufferedInputStream rozszerzając ją o możliwości odczytu typów prostych, dzięki czemu dostajemy dostęp do takich metod jak readBoolean, readDouble, readUTF, itp.

Zobaczmy jak wygląda hierarchia InputStream:

java_inputstream

I teraz zasadnicze pytanie uzmysławiające cały sens wykorzystywania dekoratorów:  w jaki sposób można byłoby zaimplementować odczyt typów prostych z wykorzystywaniem buforowania?

No właśnie. Czy lepszym rozwiązaniem byłoby zrobienie jednej klasy i dodawanie do niej kolejnych opcji i metod? Zobaczcie jak np. wygląda implementacja samego buforowania w Javie:

Dekoratory są obiektami tworzonymi w czasie działania programu i mogą być łączone w różne kombinacje bezpośrednio przy użyciu.

Dzięki kompozycji, a w tym wypadku z wykorzystaniem wzorca Dekorator oprogramowanie kolejnych zachowań odbywa się przez utworzenie nowej klasy zamiast edycji istniejącej. Tak, to jedna z zasad SOLID – zasada Open/Closed.

Ciekawostka

Mnemonik SOLID zaproponował Robert C. Martin aby w łatwy sposób zapamiętać pięć głównych założeń programowania obiektowego.

I to jedną z książek właśnie Roberta C. Martina polecam każdemu programiście: Czysty kod. Podręcznik dobrego programisty

 

Zróbmy własną implementację InputStream

Załóżmy, że chcemy wykonać pewnego rodzaju filtr / konwerter tekstu pobieranego przez strumień na wielkie litery.

Na dysku zapisałem plik tekstowy z kodowaniem UTF-8 o treści:

Odczyt pliku (z pominięciem obsługi wyjątków):

Wynik:

 

Implementacja dekoratora

Dekoratory można wykorzystać np. to oprogramowania kalkulacji ceny pizzy 🙂 No to zaczynamy!

Definiujemy klasę abstrakcyjnego produktu:

I tworzymy nasz produkt którym będzie pizza:

Teraz możemy utworzyć abstrakcyjnego dekoratora:

Czas na dodatki

W tym momencie przyszedł czas na zaimplementowanie swoich własnych dodatków i określenie dla nich ceny. Poniżej implementacja moich składników pizzy:

  • podwójny ser
  • oliwki
  • szynka parmeńska

 Czas na złożenie zamówienia

 

Wynik naszego programu:

A może jakiś rabat?

Tak, w naszym przypadku możemy przygotować prostą implementację naliczania rabatu:

Zdefiniowanie zamówień z rabatem 10% i bez żadnego rabatu:

Wynik:

 

Podsumowanie

Dziedziczenie jest formą rozszerzania klas, ale niekoniecznie musi być najlepszym sposobem na elastyczne projekty. Tworząc program warto zwracać uwagę aby możliwe było rozszerzanie zachowań bez konieczności modyfikacji istniejącego kodu. Taki efekt uzyskamy wykorzystując kompozycję i delegację.

Wzorzec projektowy dekorator stanowi poważną alternatywę dla dziedziczenia pod względem dodawania nowych zachowań. Ważną informacją jest to, że dekoratory są przezroczyste dla klientów danego składnika tak długo, jak długo funkcjonowanie klienta nie jest uzależnione od rzeczywistej implementacji danego składnika.

Na koniec mała uwaga: zastosowanie dekoratorów może być przyczyną pojawienia się w projekcie dużej ilości małych obiektów, a nadużywanie dekoratorów może doprowadzić do wzrostu złożoności kodu.

To również może Cię zainteresować:

  • Wzorce projektowe – singletonWzorce projektowe – singleton Singleton - jeden ze wzorców konstrukcyjnych. Jego celem jest zapewnienie możliwości utworzenia tylko jednego obiektu danej klasy i zapewnienie do niego globalnego dostępu. Przez wielu […]
  • Wzorce projektowe – fabrykiWzorce projektowe – fabryki Za każdym razem gdy używamy w kodzie operatora new, uzależniamy się od konkretnej implementacji zamiast od interfejsu. Jedna z zasad programowania obiektowego (reguła odwracania zależności […]
  • Arduino – zdalne sterowanie oświetleniemArduino – zdalne sterowanie oświetleniem W tym artykule opiszę sposób wykonania zdalnego sterowania jednym urządzeniem wpinanym do gniazdka 230V z wykorzystaniem Arduino. Wykorzystane będzie połączenie przez USB z komputerem, […]
  • HTML, CSS 2.1 i dokumenty PDFHTML, CSS 2.1 i dokumenty PDF W ostatnim czasie realizowałem projekt w którym należało zaprojektować ok. sto dokumentów PDF w postaci różnego rodzaju wniosków, w różnym układzie. Ten, kto kiedykolwiek generował […]
  • Interaktywne środowisko do poznawania języków programowaniaInteraktywne środowisko do poznawania języków programowania Całkiem przyjemne narzędzie do testowania składni różnych języków programowania w interaktywnym środowisku. Uruchamiasz i piszesz kod bezpośrednio w przeglądarce. W różnych językach. […]
  • Java JNA #2, Total CommanderJava JNA #2, Total Commander W poprzednim wpisie zrobiłem krótkie wprowadzenie do wykorzystywania biblioteki user32.dll przez Javę do pobierania informacji z innych aplikacji okienkowych. Poniżej rozszerzymy nasze […]

2 thoughts on “Wzorce projektowe – dekoratory

  1. Świetny art. Bardzo mi pomógł!.
    Mam tylko pytanie: Czy istnieje jakaś opcja na to aby wyłączyć działanie dekoratora na klasie?
    Tak abym miał możliwość np przyciskiem wyłączyć działanie dekorator -> tak aby klasa zachowywała się już jak normalna, nie dekorowana klasa.
    Pozdrawiam.

Dodaj komentarz

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