Decorator-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy. Lektion 96 von 110.
Das Decorator-Pattern ermöglicht es Ihnen, Objekten dynamisch neue Verhaltensweisen hinzuzufügen, indem Sie diese in spezielle Objekte, sogenannte Decorator, einhüllen. Im Gegensatz zur Vererbung, die Verhalten zur Kompilierzeit hinzufügt, hängen Decorator zusätzliche Verantwortlichkeiten zur Laufzeit an, ohne die ursprüngliche Klasse zu verändern.
Die Kernidee ist, dass sowohl das ursprüngliche Objekt als auch Dekoratoren dieselbe Schnittstelle implementieren.
Ein Decorator umhüllt das ursprüngliche Objekt, delegiert an dieses und fügt davor oder danach eigenes Verhalten hinzu:
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()); // Simple Coffee, Milk, Sugar
print(coffee.getCost()); // 2.7
}Jeder Decorator umhüllt das vorherige Objekt und erweitert es. Sie können mehrere Decorator in beliebiger Kombination stapeln und so ein flexibles Verhalten erzeugen, ohne für jede mögliche Kombination eine Unterklasse erstellen zu müssen. Dieses Muster ist ideal, wenn Sie Objekten Funktionen hinzufügen müssen, ohne andere Objekte derselben Klasse zu beeinflussen.
Aufgabe
EinfachErstellen wir ein Pizza-Bestellsystem mithilfe des Decorator-Musters! Du erstellst eine Basispizza, die mit verschiedenen Belägen erweitert werden kann, wobei jeder Belag das vorherige Element umschließt und sowohl die Beschreibung als auch den Preis ergänzt.
Du organisierst deinen Code in zwei Dateien:
pizza.dart: Diese Datei enthält deine Pizza-Schnittstelle und alle Decorator-Klassen. Beginne mit einer abstrakten KlassePizza, die zwei Methoden definiert:String getDescription()unddouble getPrice(). Erstelle anschließend eine KlassePlainPizza, diePizzaimplementiert – sie gibtPlain Pizzaals Beschreibung zurück und kostet8.0. Erstelle nun drei Decorator-Klassen, die jeweils einePizzaumschließen und erweitern:CheeseDecorator– fügt der Beschreibung, Cheeseund dem Preis1.5hinzuPepperoniDecorator– fügt der Beschreibung, Pepperoniund dem Preis2.0hinzuMushroomDecorator– fügt der Beschreibung, Mushroomsund dem Preis1.25hinzu
Pizzaimplementieren, einePizzain seinem Konstruktor akzeptieren und an die umschlossene Pizza delegieren, bevor er seinen eigenen Beitrag hinzufügt.main.dart: Importiere deine Pizzadatei und zeige, wie Decorators kombiniert werden, um eine individuelle Bestellung zu erstellen. Beginne mit einerPlainPizza, umschließe sie dann mitCheeseDecorator, umschließe diese anschließend mitPepperoniDecoratorund umschließe schließlich alles mitMushroomDecorator. Gib die Beschreibung der fertigen Pizza aus und gib anschließendTotal: $gefolgt vom Preis aus.
Beachte, wie jeder Decorator auf dem vorherigen aufbaut – du erstellst eine vollständig belegte Pizza, indem du einfache Decorators mit jeweils einer einzigen Verantwortung kombinierst!
Erwartete Ausgabe:
Plain Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.75Probier es selbst
import 'pizza.dart';
void main() {
// TODO: Erstelle eine PlainPizza
// TODO: Umhülle sie mit CheeseDecorator
// TODO: Umhülle das mit PepperoniDecorator
// TODO: Umhülle alles mit MushroomDecorator
// TODO: Gib die Beschreibung der fertigen Pizza aus
// TODO: Gib "Total: $" gefolgt vom Preis aus
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas Schlüsselwort thisMethodenInstanzvariablenGrundlagen von KonstruktorenRückblick – Einfacher Taschenrechner4Null-Sicherheit
Einführung in die Null-SicherheitNullable vs. Non-NullableDie Operatoren ? und !Late-Schlüsselwort und Null-SicherheitNull-bewusste OperatorenNull-Sicherheit in KlassenRückblick – Benutzers профilsystem7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrere InterfacesZusammenfassung – Formrechner10Collections & Generics
Übersicht über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische EinschränkungenIterable & IteratorRückblick – Generischer Speicher13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungErweiterungsmethodenAufrufbare KlassenVersiegelte Klassen (Dart 3)Records (Dart 3)Muster & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
StandardkonstruktorBenannte KonstruktorenInitialisierungslistenKonstantenkonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenRückblick – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _-Präfix-KonventionBibliotheksweite SichtbarkeitTiefergehende Getter & SetterInformationsverbergungRückblick – Studierendendaten8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas Schlüsselwort „on“ in MixinsMixin vs. VererbungMixin vs. SchnittstelleRückblick – Tiersystem11Spezielle Methoden
toString()-ÜberschreibunghashCode- & ==-ÜberschreibungComparable-Schnittstellecall()-MethodenoSuchMethod-ÜberschreibungWiederholung – Benutzerdefinierte Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFabrikmusterBeobachtermusterStrategiemuster17Abschließende Herausforderungen
E-Learning-PlattformBankingsystemSpielcharaktersystemFahrzeugvermietung3Klassen-Eigenschaften
Instanz- vs. statische MemberFinal- und Const-FelderLate-VariablenStatische Methoden und FelderGetter und SetterRückblick – Bankkontoverwaltung6Vererbung
Grundlegende VererbungDas Schlüsselwort superMethodenüberschreibungDie Annotation @overrideDas Schlüsselwort final für KlassenKonstruktoren und VererbungZusammenfassung – Mitarbeiterhierarchie9Polymorphismus
Grundlagen des PolymorphismusPolymorphismus über InterfacesLaufzeit-TypüberprüfungDie Operatoren is & asCovariant-SchlüsselwortZusammenfassung – Zahlungsprozessor12Asynchrone OOP
Futures & async/awaitStream-GrundlagenStream-ControllerAsynchrone KonstruktorenAsync in KlassenmethodenZusammenfassung – Datenabruf15Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterState-MusterComposite-MusterRepository-MusterÜbe selbstständig: Online-Dart-Compiler