Befehl-Muster
Teil des Abschnitts Objektorientierte Programmierung der GDScript-Journey von Coddy. Lektion 50 von 65.
Das Command-Muster verwandelt eine Aktion in ein Objekt. Das Objekt speichert alles, was zum Ausführen der Aktion benötigt wird, sodass du sie in einer Liste speichern, später ausführen oder rückgängig machen kannst.
Jeder Befehl hat dieselben zwei Methoden, die von einer abstrakten übergeordneten Klasse deklariert werden:
# command.gd
@abstract
class_name Command
extends RefCounted
@abstract func execute() -> void
@abstract func undo() -> voidEin konkreter Befehl speichert das Objekt, auf das er angewendet wird, den sogenannten Empfänger, sowie die Daten der Aktion. Hier ist der Empfänger ein Hero mit einem var x := 0:
# move_command.gd
class_name MoveCommand
extends Command
var hero: Hero
var dx: int
func _init(target: Hero, step: int) -> void:
hero = target
dx = step
func execute() -> void:
hero.x += dx
func undo() -> void:
hero.x -= dxDer Aufrufer führt Befehle aus und merkt sie sich. Er muss nie wissen, was ein Befehl bewirkt:
# command_history.gd
class_name CommandHistory
extends RefCounted
var done: Array[Command] = []
func run(command: Command) -> void:
command.execute()
done.append(command)
func undo_last() -> void:
if done.is_empty():
return
var last: Command = done.pop_back()
last.undo()# main.gd
var hero := Hero.new()
var history := CommandHistory.new()
history.run(MoveCommand.new(hero, 3))
history.run(MoveCommand.new(hero, 2))
print(hero.x)
history.undo_last()
print(hero.x)5
3Der Verlauf ist ein Stapel: pop_back() nimmt den neuesten Befehl, sodass beim Rückgängigmachen die Aktionen in umgekehrter Reihenfolge durchlaufen werden. Dieselben Objekte ermöglichen dir auch eine Neubelegung der Tasten (eine Taste wird einem Befehl zugeordnet), Wiederholungen (die gespeicherte Liste wird erneut ausgeführt) und Zugwarteschlangen.
Referenzen verlaufen nur in eine Richtung: Die Befehle verweisen auf den Helden, und der Held weiß nichts von ihnen. Wenn der Held seine Befehle ebenfalls speichern würde, würden die Objekte sich gegenseitig am Leben erhalten und niemals freigegeben werden.
Aufgabe
EinfachEine Spielfigur bewegt sich auf einem Raster, und jeder Zug kann rückgängig gemacht werden. Command (command.gd) deklariert execute() und undo(), und Hero (hero.gd) hat x, y und position_text(). Schreibe:
MoveCommand: Der Konstruktor speichert die Spielfigur und die Schrittestep_xundstep_y.execute()addiert die Schritte zuxundyder Spielfigur, undundo()zieht sie ab.CommandHistory:run(command)führt den Befehl aus und speichert ihn.undo_last()macht den zuletzt gespeicherten Befehl rückgängig, entfernt ihn und gibttruezurück; wenn nichts gespeichert ist, gibt die Methodefalsezurück.
Der bereitgestellte Code liest, wie viele Aktionen folgen, dann eine Aktion pro Zeile (up, down, left, right oder undo) und gibt nach jeder Aktion die Position aus. up verringert y um eins. Für right, up, undo, undo, undo lautet die Ausgabe:
right -> (1, 0)
up -> (1, -1)
undo -> (1, 0)
undo -> (0, 0)
Nothing to undoDein Code gehört in move_command.gd und command_history.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 hero := Hero.new()
var history := CommandHistory.new()
var steps := {"up": [0, -1], "down": [0, 1], "left": [-1, 0], "right": [1, 0]}
var count := int(OS.read_string_from_stdin())
for i in count:
var action := OS.read_string_from_stdin()
if action == "undo":
if history.undo_last():
print("undo -> ", hero.position_text())
else:
print("Nothing to undo")
else:
var step: Array = steps[action]
history.run(MoveCommand.new(hero, step[0], step[1]))
print(action, " -> ", hero.position_text())
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