Menu
Coddy logo textTech

Wzorzec dekoratora

Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 58 z 70.

Wzorzec dekoratora to wzorzec strukturalny, który pozwala dołączać nowe zachowania do obiektów przez opakowywanie ich w specjalne obiekty zwane dekoratorami. W przeciwieństwie do dziedziczenia, które dodaje zachowanie na etapie kompilacji, dekoratory dodają funkcjonalność dynamicznie w czasie działania, bez modyfikowania oryginalnej klasy.

Wzorzec działa dzięki temu, że dekoratory implementują ten sam interfejs co obiekt, który opakowują. Każdy dekorator przechowuje referencję do opakowanego obiektu i deleguje do niego wywołania, dodając własne zachowanie przed nimi lub po nich:

public interface ICoffee
{
    string GetDescription();
    decimal GetCost();
}

public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Coffee";
    public decimal GetCost() => 2.00m;
}

public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    
    public CoffeeDecorator(ICoffee coffee) => _coffee = coffee;
    
    public virtual string GetDescription() => _coffee.GetDescription();
    public virtual decimal GetCost() => _coffee.GetCost();
}

public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }
    
    public override string GetDescription() => _coffee.GetDescription() + ", Milk";
    public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}

Siła dekoratorów tkwi w możliwości ich nakładania. Możesz opakować udekorowany obiekt kolejnym dekoratorem:

ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee);  // Podwójne mleko!

Console.WriteLine(coffee.GetDescription());  // Kawa, mleko, mleko
Console.WriteLine(coffee.GetCost());         // 3.00

Wzorzec Dekorator sprawdza się idealnie, gdy chcesz dodawać obiektom odpowiedzialności bez tworzenia ogromnej liczby podklas. Każdy dekorator skupia się na jednym aspekcie, a Ty łączysz je w razie potrzeby, aby uzyskać dokładnie takie zachowanie, jakiego chcesz.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system zamawiania pizzy z użyciem wzorca dekoratora. Utworzysz bazową pizzę, a następnie będziesz dynamicznie dodawać do niej dodatki — każdy dodatek będzie opakowywać poprzednią warstwę oraz wzbogacać opis i zwiększać cenę.

Rozmieścisz kod w trzech plikach:

  • Pizza.cs: Zdefiniuj interfejs IPizza w przestrzeni nazw PizzaShop z dwiema metodami: GetDescription(), która zwraca ciąg znaków, oraz GetPrice(), która zwraca liczbę dziesiętną. Następnie utwórz klasę PlainPizza, która implementuje ten interfejs — powinna zwracać "Pizza" jako opis i 8.00m jako cenę bazową.
  • ToppingDecorators.cs: Utwórz abstrakcyjną klasę ToppingDecorator, która implementuje IPizza i przechowuje referencję do opakowywanej pizzy (przekazywaną przez konstruktor). Jej metody wirtualne powinny delegować wywołania do opakowywanej pizzy. Następnie utwórz trzy konkretne dekoratory:
    • CheeseTopping — dopisuje ", Cheese" do opisu i dodaje 1.50m do ceny
    • PepperoniTopping — dopisuje ", Pepperoni" do opisu i dodaje 2.00m do ceny
    • MushroomTopping — dopisuje ", Mushrooms" do opisu i dodaje 1.25m do ceny
  • Program.cs: Połącz wszystkie elementy, zaczynając od zwykłej pizzy i opakowując ją dodatkami wskazanymi w danych wejściowych. Każdy dekorator dodatku opakowuje poprzednią pizzę, warstwa po warstwie budując końcowe zamówienie.

Otrzymasz jedno dane wejściowe:

  • Rozdzieloną przecinkami listę dodatków do dodania (np. cheese,pepperoni,cheese)

Zacznij od PlainPizza, a następnie opakuj ją odpowiednim dekoratorem dla każdego dodatku, w kolejności, w jakiej występuje na liście. Prawidłowe dodatki to cheese, pepperoni i mushrooms. Po zastosowaniu wszystkich dodatków wypisz opis w jednej linii, a łączną cenę w następnej.

Na przykład, jeśli dane wejściowe to cheese,pepperoni,mushrooms, wynikiem powinno być:

Pizza, Cheese, Pepperoni, Mushrooms
12.75

Zauważ, że ten sam dodatek możesz dodać wiele razy! Jeśli dane wejściowe to cheese,cheese, otrzymasz:

Pizza, Cheese, Cheese
11.00

To pokazuje elastyczność wzorca dekoratora — każdy dekorator opakowuje otrzymaną pizzę, niezależnie od tego, czy jest to zwykła pizza, czy już udekorowana, dzięki czemu możesz dynamicznie nakładać kolejne warstwy!

Spróbuj swoich sił

using System;
using PizzaShop;

class Program
{
    public static void Main(string[] args)
    {
        // Odczytaj listę dodatków oddzielonych przecinkami
        string input = Console.ReadLine();
        string[] toppings = input.Split(',');
        
        // TODO: Zacznij od PlainPizza
        
        // TODO: Przejdź przez każdy dodatek i opakuj pizzę
        // odpowiednim dekoratorem na podstawie nazwy dodatku:
        // - "cheese" -> CheeseTopping
        // - "pepperoni" -> PepperoniTopping
        // - "mushrooms" -> MushroomTopping
        
        // TODO: Wyświetl końcowy opis i cenę
    }
}
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 C# online