Menu
Coddy logo textTech

Wzorzec dekoratora

Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 77 z 91.

Wzorzec Dekoratora to strukturalny wzorzec projektowy, który pozwala dynamicznie dodawać nowe zachowania do obiektów przez opakowywanie ich w specjalne obiekty opakowujące zwane dekoratorami. W przeciwieństwie do dziedziczenia, które dodaje zachowanie w czasie kompilacji, dekoratory dodają zachowanie w czasie wykonywania.

Wyobraź sobie kawiarnię. Zaczynasz od podstawowej kawy, a potem dodajesz dodatki, takie jak mleko, cukier lub bita śmietana.

Każdy dodatek otacza oryginalną kawę oraz rozszerza jej opis i zwiększa jej koszt. Kluczowe jest to, że zarówno kawa bazowa, jak i dekoratory korzystają z tego samego interfejsu.

<?php
interface Coffee {
    public function getDescription(): string;
    public function getCost(): float;
}

class SimpleCoffee implements Coffee {
    public function getDescription(): string {
        return "Simple Coffee";
    }
    
    public function getCost(): float {
        return 2.00;
    }
}

Dekoratory implementują ten sam interfejs i opakowują inny obiekt Coffee:

<?php
class MilkDecorator implements Coffee {
    public function __construct(private Coffee $coffee) {}
    
    public function getDescription(): string {
        return $this->coffee->getDescription() . ", Milk";
    }
    
    public function getCost(): float {
        return $this->coffee->getCost() + 0.50;
    }
}

class SugarDecorator implements Coffee {
    public function __construct(private Coffee $coffee) {}
    
    public function getDescription(): string {
        return $this->coffee->getDescription() . ", Sugar";
    }
    
    public function getCost(): float {
        return $this->coffee->getCost() + 0.25;
    }
}

$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);

echo $coffee->getDescription() . " = $" . $coffee->getCost();

Wynik:

Simple Coffee, Milk, Sugar = $2.75

Każdy dekorator opakowuje poprzedni obiekt, tworząc łańcuch. Ten wzorzec pozwala łączyć zachowania w dowolnej kolejności i liczbie, bez tworzenia klasy dla każdej możliwej kombinacji. Chcesz podwójną porcję mleka? Po prostu opakuj go dwa razy.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system zamawiania pizzy z użyciem wzorca Dekoratora. Zaczniesz od podstawowej pizzy i będziesz dynamicznie dodawać dodatki: każdy dodatek opakowuje pizzę i rozszerza jej opis oraz cenę, tak jak w przykładzie z kawą z lekcji.

Rozmieścisz swój kod w czterech plikach:

  • Pizza.php: Zdefiniuj interfejs Pizza, który określa kontrakt dla wszystkich pizz i dodatków. Powinien mieć dwie metody: getDescription(): string i getPrice(): float.
  • BasePizza.php: Dołącz interfejs Pizza i utwórz klasę MargheritaPizza, która go implementuje. To twoja podstawowa pizza z opisem "Margherita" i ceną 8.00.
  • Toppings.php: Dołącz interfejs Pizza i utwórz trzy klasy dekoratorów, które go implementują. Każdy dekorator opakowuje obiekt Pizza (przyjmuje go w konstruktorze) i rozszerza zarówno opis, jak i cenę:
    • CheeseTopping: Dodaje ", Extra Cheese" do opisu i 1.50 do ceny
    • PepperoniTopping: Dodaje ", Pepperoni" do opisu i 2.00 do ceny
    • MushroomTopping: Dodaje ", Mushrooms" do opisu i 1.25 do ceny
  • main.php: Dołącz pliki BasePizza i Toppings. Otrzymasz jedno wejście: listę dodatków do dodania, oddzielonych przecinkami (np. "cheese,pepperoni" lub "mushroom,cheese,cheese").

    Zacznij od MargheritaPizza. Dla każdego dodatku z wejścia opakuj pizzę odpowiednim dekoratorem:

    • "cheese" opakowuje ją za pomocą CheeseTopping
    • "pepperoni" opakowuje ją za pomocą PepperoniTopping
    • "mushroom" opakowuje ją za pomocą MushroomTopping

    Po dodaniu wszystkich dodatków wypisz opis końcowej pizzy i jej cenę w tym formacie:

    [description] = $[price]

    Sformatuj cenę do dwóch miejsc po przecinku.

Zwróć uwagę, że ten sam dodatek można zastosować wiele razy: zamówienie podwójnej porcji sera oznacza po prostu dwukrotne opakowanie pizzy za pomocą CheeseTopping. Na tym polega elastyczność wzorca Dekoratora!

Spróbuj swoich sił

<?php
require_once 'BasePizza.php';
require_once 'Toppings.php';

// Wczytaj dane wejściowe – listę dodatków oddzielonych przecinkami
$input = trim(fgets(STDIN));
$toppings = explode(',', $input);

// Zacznij od podstawowej MargheritaPizza
$pizza = new MargheritaPizza();

// TODO: Przejdź przez każdy dodatek i owiń pizzę odpowiednim dekoratorem
// - "cheese" -> owiń dekoratorem CheeseTopping
// - "pepperoni" -> owiń dekoratorem PepperoniTopping
// - "mushroom" -> owiń dekoratorem MushroomTopping

// TODO: Wypisz wynik w formacie: [description] = $[price]
// Sformatuj cenę do 2 miejsc po przecinku za pomocą number_format()

?>
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator PHP online