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.00Wzorzec 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.
Wyzwanie
ŁatwyZbudujmy 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 interfejsIPizzaw przestrzeni nazwPizzaShopz dwiema metodami:GetDescription(), która zwraca ciąg znaków, orazGetPrice(), która zwraca liczbę dziesiętną. Następnie utwórz klasęPlainPizza, która implementuje ten interfejs — powinna zwracać"Pizza"jako opis i8.00mjako cenę bazową.ToppingDecorators.cs: Utwórz abstrakcyjną klasęToppingDecorator, która implementujeIPizzai 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 dodaje1.50mdo cenyPepperoniTopping— dopisuje", Pepperoni"do opisu i dodaje2.00mdo cenyMushroomTopping— dopisuje", Mushrooms"do opisu i dodaje1.25mdo 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.75Zauważ, że ten sam dodatek możesz dodać wiele razy! Jeśli dane wejściowe to cheese,cheese, otrzymasz:
Pizza, Cheese, Cheese
11.00To 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ę
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online