Strategie-Muster
Teil des Abschnitts Objektorientierte Programmierung der GDScript-Journey von Coddy. Lektion 49 von 65.
Das Strategy-Muster legt jede Version eines Algorithmus in ein eigenes Objekt, sodass du den Algorithmus austauschen kannst, während das Programm läuft. Das Objekt, das ihn verwendet, der Kontext, ruft eine Methode auf und kümmert sich nicht darum, welche Version es enthält.
Hier sind zwei Angriffsstrategien: eine Elternklasse und eine Unterklasse mit derselben Methode damage():
# attack_style.gd
class_name AttackStyle
extends RefCounted
func damage(power: int) -> int:
return power# heavy_attack.gd
class_name HeavyAttack
extends AttackStyle
func damage(power: int) -> int:
return power * 2Der Kontext speichert eine Strategie und überlässt ihr die Berechnung:
# fighter.gd
class_name Fighter
extends RefCounted
var power := 10
var style: AttackStyle = AttackStyle.new()
func set_style(new_style: AttackStyle) -> void:
style = new_style
func attack() -> void:
print("Hit for ", style.damage(power))# main.gd
var fighter := Fighter.new()
fighter.attack()
fighter.set_style(HeavyAttack.new())
fighter.attack()Hit for 10
Hit for 20Fighter prüft nie, welchen Stil es verwendet. Ein neuer Stil ist eine neue Klasse, und fighter.gd bleibt unverändert; eine if style == "heavy"-Kette innerhalb von attack() würde mit jedem Stil länger werden.
Wenn eine Strategie eine kleine Funktion ohne eigene Daten ist, reicht ein Callable aus. Der Kontext speichert die Funktion und führt sie mit call() aus:
# price_calculator.gd
class_name PriceCalculator
extends RefCounted
var discount: Callable = func(price): return price
func final_price(price: int) -> int:
return discount.call(price)# main.gd
var calc := PriceCalculator.new()
print(calc.final_price(80))
calc.discount = func(price): return price / 2
print(calc.final_price(80))
calc.discount = func(price): return max(price - 25, 0)
print(calc.final_price(80))80
40
55Verwende Klassen, wenn eine Strategie eigene Einstellungen oder mehrere Methoden hat, und Callables für eine kurze Berechnung. Du hast diese Idee bereits verwendet: Die Vergleichsfunktion, die du an sort_custom() übergibst, ist eine Sortierstrategie.
Aufgabe
MittelEin Warenkorb kann beim Bezahlen zwischen Rabattstrategien wechseln. Discount in discount.gd ist die Basisstrategie: kein Rabatt. Schreibe zwei weitere Strategien und den Warenkorb:
PercentOff(wird mit einem Prozentsatz erstellt):apply(total)gibttotal - total * percent / 100zurück (Ganzzahlarithmetik), undlabel()gibt bei 10 Prozent10% offzurück.FixedOff(wird mit einem Betrag erstellt):apply(total)gibt den Gesamtbetrag abzüglich des Betrags zurück, jedoch niemals weniger als0, undlabel()gibt bei einem Betrag von 3030 offzurück.Cart:set_discount(new_discount)speichert die Strategie, undcheckout()addiert die Preise und gibtSubtotal TOTAL, LABEL, pay PRICEzurück, wobei PRICE dem vonapply()der Strategie zurückgegebenen Wert entspricht.
Der bereitgestellte Code liest die Preise in einer Zeile, die Anzahl der folgenden Bezahlvorgänge und eine Strategie pro Zeile ein: percent N, fixed N oder none. Für die Preise 50 30 40 und die Strategien percent 10, fixed 30, none lautet die Ausgabe:
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Dein Code gehört in cart.gd, percent_off.gd und fixed_off.gd. main.gd enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
extends Node
func _ready():
# Vorgegebener Ein-/Ausgabecode: unverändert lassen
var cart := Cart.new()
for price in OS.read_string_from_stdin().split(" "):
cart.add_item(int(price))
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "percent":
cart.set_discount(PercentOff.new(int(parts[1])))
elif parts[0] == "fixed":
cart.set_discount(FixedOff.new(int(parts[1])))
else:
cart.set_discount(Discount.new())
print(cart.checkout())
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 – Formrechner2Klassenattribute
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