Wzorzec decorator należy do wzorców strukturalnych. W praktyce umożliwia dynamiczne dodawanie nowych funkcji do istniejących klas bez ich fizycznej modyfikacji. Dzięki temu można rozszerzyć możliwości klasy bez ingerowania w jej kod pozostawiając jej oryginalną funkcjonalność.

Aby pokazać działanie wzorca decorator posłużę się prostym przykładem. Załóżmy, że mam klasę zapisującą dane do bazy. Nie chcę zmieniać jej kodu i funkcjonalności ale chcę ją rozszerzyć o walidację danych. Mógłbym zastosować dziedziczenie i wykorzystać dziedziczoną metodą do walidacji ale wówczas musiałbym zmodyfikować kod klasy głównej czego bardzo chcę uniknąć a dodatkowo każda zmiana klasy dziedziczącej wymuszałaby zmiany klas dziedziczonych.

Zacznę od stworzenia wspólnego interfejsu dla klasy dekorowanej i dekoratora:

PHP:
  1. interface IDecorator
  2. {
  3.     function save();
  4. }

Następnie stworzę klasę główną (dekorowaną) implementującą interfejs IDecorator, której kodu podczas rozszerzania nie mam zamiaru modyfikować:

PHP:
  1. class Decorable imlements IDecorator
  2. {
  3.     function save()
  4.     {
  5.         // zapis danych do bazy
  6.         // [...]
  7.     }
  8. }

Teraz przyszła kolej na stworzenie klasy dekorującej czyli wzbogacającej klasę główną Decorable o funkcję walidującą dane, która także implementuje interfejs IDecorator:

PHP:
  1. class Decorate implements IDecorator
  2. {
  3.     /**
  4.      * Obiekt klasy dekorowanej
  5.      *
  6.      * @var object
  7.      * @access private
  8.      */
  9.     private $_decorate;
  10.  
  11.     /**
  12.      * Konstruktor klasy
  13.      *
  14.      * @param object $decorable
  15.      */
  16.     function __construct(Decorable $decorable)
  17.     {
  18.         // przypisanie obiektu klasy dekorowanej do zmiennej prywatnej
  19.         $this->_decorable = $decorable
  20.     }
  21.  
  22.     /**
  23.      * Metoda uruchamia walidacje danych
  24.      */
  25.     function save()
  26.     {
  27.         // walidacja danych
  28.         // [...]
  29.  
  30.         // wywolanie oryginalnej metody z klasy dekorowanej
  31.         $this->_decorable->save();
  32.     }
  33. }

Do konstruktora klasy zostaje przekazany obiekt oryginalnej klasy dekorowanej Decorable. Na koniec pozostaje mi stworzenie obiektu klasy dekorowanej Decorable i przekazanie jej klasie dekorującej Decorate:

PHP:
  1. $action = new Decorate(new Decorable());
  2. $action->save();

Po stworzeniu obiektu klasy Decorable wywołuję metodę save(), która po przeprowadzeniu walidacji wywołuje metodę o tej samej nazwie klasy dekorowanej Decorable.

Jak więc widać rozszerzyłem funkcjonalność klasy oryginalnej nie dokonując w niej żadnych istotnych zmian. Podsumowując dekoratory są w pewnym sensie alternatywą dla dziedziczenia. Dziedziczenie rozszerza zachowanie klasy w trakcie kompilacji, w przeciwieństwie do dekoratorów, które rozszerzają klasy w czasie działania programu.

Więcej o wzorcu dekoratora:
http://pl.wikipedia.org/wiki/Wzorzec_dekoratora
http://4programmers.net/PHP/Wzorce_Projektowe#id-Dekorator
http://www.phppatterns.com/docs/design/decorator_pattern
http://www.fluffycat.com/PHP-Design-Patterns/Decorator
http://www.vincehuston.org/dp/decorator.html

3 Responses to “Wzorce projektowe - decorator”

  1. flashmaniak

    Super opisane…..!
    Dobra robota :)

    October 30th, 2007 | 20:55
  2. markac

    “Mógłbym zastosować dziedziczenie i wykorzystać dziedziczoną metodą do walidacji ale wówczas musiałbym zmodyfikować kod klasy głównej czego bardzo chcę uniknąć a dodatkowo każda zmiana klasy dziedziczącej wymuszałaby zmiany klas dziedziczonych.”

    A to ciekawe. A niby co chciałbyś zmienić w klasie bazowej i jaką metodę validujacć wykorzystać, jeśli klasa bazowa jej nie ma? Przecież dopiero chcesz ją stworzyć…?
    Proponuje stworzyć drugi przykład oparty na dziedziczeniu, bo wydaje mi się, ze na sile ten przykład był pisany.

    October 29th, 2008 | 12:36
  3. markac

    Przykład:

    class Decorable {
    function save() {…}
    }

    class Decorator extends Decorable {
    function validate() {…}

    function save() {
    $this->validate();
    $parent::save();
    }
    }

    Czy coś zmieniłem w klasie bazowej? :)

    October 29th, 2008 | 12:40

Leave a Reply

You must be logged in to post a comment.