Warstwa Abstrakcji - stanowi izolację pomiędzy kodem programisty a zewnętrznymi modułami, pozwala odseparować system od jego zależności przez co czyni go podatnym na zmiany i aktualizacje. Przywiązując system do innej usługi czynimy go słabym.

Dekorator

Typ: Strukturalny

Wyobraźmy sobie sytuację, gdzie kombinacja wielu dodatków do bazowej klasy spowodowałaby rozlew klas dziedziczących, które programista musiałby zaimplementować w aplikacji, na przykład: PizzaWithPepperoniAndTabasco extends Pizza, PizzaWithPepperoniAndMushrooms extends Pizza i tak dalej. Można sobie łatwo wyobrazić jak wiele typowa pizzeria może stworzyć kombinacji tego smacznego placka. Z pomocą przychodzi wzorzec dekorator, który umożliwia nam rozszerzanie funkcjonalności obiektu w czasie pracy aplikacji.

Przykładowa implementacja

interface SomeFancyInterface
{
    public function getInfo();
}

class BasicObject implements SomeFancyInterface
{
    public function getInfo()
    {
        return "I'm basic object";
    }
}

class Decorator implements SomeFancyInterface
{
    private $object;

    public function __construct(SomeFancyInterface $object)
    {
        $this->object = $object;
    }

    public function getInfo()
    {
        return $this->object->getInfo() . " decorated!";
    }
}

$object = new BasicObject();
$decoratedObject = new Decorator($object);

echo $decoratedObject->getInfo() . PHP_EOL; // I'm basic object decorated!

Kiedy używać

Dekorator jest niezwykle użyteczny w momencie kiedy naturalnym zachowaniem pewnego obiektu jest jego podatność na rozszerzenia. Można wyobrazić sobie wiele przypadków użycia, jak na przykład proces zakupu samochodu (dodwanie nowych opcji, jak nawigacja, przyciemniane szyby), wspomniana wyżej pizzeria, polisa ubezpieczeniowa itp. Zaprojektujmy zatem banalny model dla sprzedaży telewizji kablowej, skupmy się na ofercie.

Przykład użycia

Specyfikacja

  • Mamy podstawową ofertę
  • Klient może do oferty dodać opcje: pakiet sportowy, edukacyjny i filmowy
  • Zbudujmy finalną ofertę za pomocą dekoratorów

Implementacja

interface Offer
{
    public function getPrice();
}

final class BaseOffer implements Offer
{
    public function getPrice()
    {
        return 40;
    }
}

final class SportDecorator implements Offer
{
    private $offer;

    public function __construct(Offer $offer)
    {
        $this->offer = $offer;
    }

    public function getPrice()
    {
        return $this->offer->getPrice() + 20;
    }
}

final class EducationDecorator implements Offer
{
    private $offer;

    public function __construct(Offer $offer)
    {
        $this->offer = $offer;
    }

    public function getPrice()
    {
        return $this->offer->getPrice() + 10;
    }
}

final class CinemaDecorator implements Offer
{
    private $offer;

    public function __construct(Offer $offer)
    {
        $this->offer = $offer;
    }

    public function getPrice()
    {
        return $this->offer->getPrice() + 30;
    }
}

$offer = new BaseOffer();
$offerWithSport = new SportDecorator($offer); 
$offerWithSportAndCinema = new CinemaDecorator(new SportDecorator($offer));
$fullOffer = new CinemaDecorator(new SportDecorator(new EducationDecorator($offer))); 

Omówienie

Wprowadziliśmy wspólny interfejs dla podstawowej klasy jak i klas dekoratorów. Z racji tego, że każdy obiekt dekorowany będzie spójny interfejsem z obiektem bazowym, możemy swobodnie dekorować obiekty zmieniając ich właściwości. Powyższy przykład został zaimplementowany przy użyciu interfejsów, ale równie dobrze można go zaimplementować przy użyciu klas abstrakcyjnych.

Błędy?

Znalazłeś błąd?
Chciałbyś podać lepszy przykład użycia?
A może nie zgadzasz się z implementacją przykładu?

Edytuj na Github.com Popraw lub zgłoś błąd

Pytania

Masz pytania odnośnie omawianego wzorca? Chciałbyś zapytać czy wzorzec pasuje do problemu, który aktualnie rozwiązujesz? Dobrze się składa, to jest idealne miejsce.

Błędy, sugestie ich poprawy, dyskusję na temat poprawności implementacji danego przykładu prosimy prowadzić poprzez Github Issues
Akceptuję

Ten serwis używa plików cookies. Więcej o plikach cookies.