Dekorierer-Muster
Teil des Abschnitts Objektorientierte Programmierung der GDScript-Journey von Coddy. Lektion 52 von 65.
Das Decorator-Muster fügt einem Objekt Verhalten hinzu, indem es in ein anderes Objekt mit denselben Methoden eingeschlossen wird. Jeder Wrapper ruft das darin enthaltene Objekt auf und fügt seinen eigenen Teil hinzu, und Wrapper können in beliebiger Reihenfolge gestapelt werden.
# weapon.gd
class_name Weapon
extends RefCounted
func get_damage() -> int:
return 10
func describe() -> String:
return "Sword"Ein Dekorator erweitert Weapon, sodass er überall dort eingesetzt werden kann, wo eine Waffe erwartet wird, und enthält die Waffe, die er umhüllt:
# fire_enchant.gd
class_name FireEnchant
extends Weapon
var inner: Weapon
func _init(wrapped: Weapon) -> void:
inner = wrapped
func get_damage() -> int:
return inner.get_damage() + 5
func describe() -> String:
return inner.describe() + " + Fire"SharpEnchant wird auf dieselbe Weise geschrieben, aber sein get_damage() gibt inner.get_damage() * 2 zurück und sein describe() fügt " + Sharp" hinzu.
# main.gd
var weapon: Weapon = Weapon.new()
print(weapon.describe(), ": ", weapon.get_damage())
weapon = FireEnchant.new(weapon)
weapon = SharpEnchant.new(weapon)
print(weapon.describe(), ": ", weapon.get_damage())Sword: 10
Sword + Fire + Sharp: 30Die Aufrufe wandern nach innen: SharpEnchant fragt FireEnchant, das wiederum das Schwert fragt. Das Schwert gibt 10 zurück, Feuer erhöht den Wert auf 15, und Schärfe verdoppelt ihn auf 30. In der anderen Reihenfolge, FireEnchant.new(SharpEnchant.new(Weapon.new())), beträgt der Schaden 10 * 2 + 5, also 25.
Ohne Decorators bräuchte jede Kombination ihre eigene Unterklasse: FireSword, SharpSword, FireSharpSword und so weiter. Mit ihnen decken zwei kleine Klassen jede Kombination ab, und eine neue Verzauberung bedeutet eine weitere Datei.
Aufgabe
MittelEin Café stellt Getränke mit Decorators zusammen. Beverage (beverage.gd) deklariert cost() und description(); Espresso (espresso.gd, $3) und Tea (tea.gd, $2) sind bereits geschrieben. Jeder Decorator speichert das Getränk, das er umhüllt, in inner (die Konstruktoren sind bereits geschrieben). Überschreibe cost() und description() in:
Milk: kostet1mehr und fügt der Beschreibung+ Milkhinzu.Mocha: kostet2mehr und fügt+ Mochahinzu.Large: verdoppelt die Kosten für alles, was sich darin befindet, und stellt der BeschreibungLargevoran.
Der bereitgestellte Code liest ein, wie viele Bestellungen folgen, und dann eine Bestellung pro Zeile: das Getränk (espresso oder tea) und seine Extras, die von links nach rechts angewendet werden. Für espresso milk large und tea mocha milk lautet die Ausgabe:
Large Espresso + Milk: $8
Tea + Mocha + Milk: $5Dein Code gehört in milk.gd, mocha.gd und large.gd. main.gd enthält den bereitgestellten Ein-/Ausgabecode und kann nicht bearbeitet werden.
Probier es selbst
extends Node
func _ready():
# Vorgegebener Eingabe-/Ausgabecode: unverändert lassen
var count := int(OS.read_string_from_stdin())
for i in count:
var words := OS.read_string_from_stdin().split(" ")
var drink: Beverage = Tea.new()
if words[0] == "espresso":
drink = Espresso.new()
for extra in words.slice(1):
match extra:
"milk":
drink = Milk.new(drink)
"mocha":
drink = Mocha.new(drink)
"large":
drink = Large.new(drink)
print("%s: $%d" % [drink.description(), drink.cost()])
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 DateienEinführung in die OOPKlassen vs. ObjekteDas Schlüsselwort selfMethodenEigenschaftenKonstruktor (_init)Wiederholung – Einfacher Taschenrechner4Polymorphie
Methodenüberschreibung erneut betrachtetDuck-TypisierungAbstrakte Klassen und MethodenSchnittstellendesignRückblick – Formrechner7Signale
Was ist ein Signal?Deklarieren und AuslösenMit Signalen verbindenSignale mit ArgumentenRückblick – Gesundheitssystem10Entwurfsmuster Teil 2
Befehl-MusterAdapter-MusterDekorierer-MusterVorlagenmethoden-MusterZustandsmusterKompositum-Muster2Klassenattribute
Instanz- vs. statische VariablenStatische FunktionenProperty-SetterProperty-GetterPrivat nach KonventionWiederholung – Bankkontenverwaltung5Kapselung
Öffentliche und private MemberValidierung mit SetternInformationsverbergenSchreibgeschützte EigenschaftenRückblick – System zur Schülerverwaltung8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungInnere KlassenNodes als KomponentenRefCounted vs. NodeEnums in Klassen11Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Vererbung
Grundlegende VererbungDas Schlüsselwort superMethodenüberschreibungVererbungskettenTypprüfungen mit isWiederholung – Mitarbeiterhierarchie6Spezielle Methoden
Einführung in virtuelle MethodenDie _to_string-MethodeBenutzerdefinierte IteratorenRückblick – Benutzerdefinierte Liste9Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBeobachter-MusterStrategie-Muster12Abschließende Herausforderungen
E-Learning-PlattformBankensystemEntwicklung von SpielfigurenFahrzeugvermietungÜbe selbstständig: Online-GDScript-Compiler