Wzorzec dekoratora
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 96 z 110.
Wzorzec Dekorator pozwala dynamicznie dodawać nowe zachowania do obiektów przez opakowywanie ich w specjalne obiekty nazywane dekoratorami. W przeciwieństwie do dziedziczenia, które dodaje zachowanie na etapie kompilacji, dekoratory dołączają dodatkowe obowiązki w czasie wykonywania, nie modyfikując oryginalnej klasy.
Kluczowa idea polega na tym, że zarówno oryginalny obiekt, jak i dekoratory implementują ten sam interfejs.
Dekorator opakowuje oryginalny obiekt, deleguje do niego i dodaje własne zachowanie przed jego działaniem lub po nim:
abstract class Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
@override
String getDescription() => 'Simple Coffee';
@override
double getCost() => 2.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Milk';
@override
double getCost() => _coffee.getCost() + 0.5;
}
class SugarDecorator implements Coffee {
final Coffee _coffee;
SugarDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Sugar';
@override
double getCost() => _coffee.getCost() + 0.2;
}
void main() {
Coffee coffee = SimpleCoffee();
coffee = MilkDecorator(coffee);
coffee = SugarDecorator(coffee);
print(coffee.getDescription()); // Prosta kawa, mleko, cukier
print(coffee.getCost()); // 2.7
}Każdy dekorator opakowuje poprzedni obiekt i wzbogaca go o dodatkowe możliwości. Możesz łączyć wiele dekoratorów w dowolnej kombinacji, uzyskując elastyczne zachowanie bez tworzenia podklasy dla każdej możliwej kombinacji. Ten wzorzec sprawdza się idealnie, gdy chcesz dodawać funkcje do obiektów bez wpływania na inne obiekty tej samej klasy.
Wyzwanie
ŁatwyZbudujmy system zamawiania pizzy przy użyciu wzorca Dekorator! Utworzysz bazową pizzę, którą można wzbogacać o różne dodatki. Każdy dodatek opakowuje poprzedni element i wpływa zarówno na opis, jak i na cenę.
Podzielisz kod na dwa pliki:
pizza.dart: Ten plik zawiera interfejs pizzy i wszystkie klasy dekoratorów. Zacznij od klasy abstrakcyjnejPizza, która definiuje dwie metody:String getDescription()idouble getPrice(). Następnie utwórz klasęPlainPizza, która implementujePizza— zwracaPlain Pizzajako opis i kosztuje8.0. Teraz utwórz trzy klasy dekoratorów, z których każda opakowujePizzai ją wzbogaca:CheeseDecorator— dodaje, Cheesedo opisu i1.5do cenyPepperoniDecorator— dodaje, Pepperonido opisu i2.0do cenyMushroomDecorator— dodaje, Mushroomsdo opisu i1.25do ceny
Pizza, przyjmowaćPizzaw konstruktorze i delegować wywołania do opakowanej pizzy, zanim doda własny składnik.main.dart: Zaimportuj plik z pizzą i pokaż, jak dekoratory nakładają się na siebie, tworząc spersonalizowane zamówienie. Zacznij odPlainPizza, następnie opakuj ją wCheeseDecorator, potem opakuj ją wPepperoniDecorator, a na koniec opakuj całość wMushroomDecorator. Wypisz opis gotowej pizzy, a następnie wypiszTotal: $, po którym podaj cenę.
Zauważ, jak każdy dekorator bazuje na poprzednim — nakładając na siebie proste dekoratory o pojedynczej odpowiedzialności, tworzysz pizzę ze wszystkimi dodatkami!
Oczekiwany wynik:
Plain Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.75Spróbuj swoich sił
import 'pizza.dart';
void main() {
// TODO: Utwórz PlainPizza
// TODO: Owiń ją za pomocą CheeseDecorator
// TODO: Owiń ją następnie za pomocą PepperoniDecorator
// TODO: Owiń całość za pomocą MushroomDecorator
// TODO: Wypisz opis gotowej pizzy
// TODO: Wypisz "Total: $", a następnie 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 OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatności12Asynchroniczne OOP
Futures i async/awaitPodstawy strumieniKontrolery strumieniKonstruktory asynchroniczneAsynchroniczność w metodach klasyPodsumowanie — pobieranie danych15Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec repozytoriumPoćwicz samodzielnie: Kompilator Dart online