Command-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 94 von 110.
Das Command-Pattern kapselt eine Anfrage als Objekt und ermöglicht es Ihnen so, Clients mit verschiedenen Anfragen zu parametrisieren, Operationen in eine Warteschlange zu stellen oder eine Rückgängig-Funktionalität zu unterstützen. Anstatt eine Methode direkt aufzurufen, kapseln Sie die Aktion in einem Command-Objekt, das gespeichert, weitergegeben oder später ausgeführt werden kann.
Das Muster umfasst drei Schlüsselkomponenten: eine Command-Schnittstelle mit einer execute-Methode, konkrete Befehle (Concrete Commands), die spezifische Aktionen implementieren, und einen Receiver, der die eigentliche Arbeit verrichtet. Ein optionaler Invoker löst Befehle aus, ohne zu wissen, was sie tun:
abstract class Command {
void execute();
}
class Light {
void turnOn() => print('Light is ON');
void turnOff() => print('Light is OFF');
}
class TurnOnCommand implements Command {
final Light light;
TurnOnCommand(this.light);
@override
void execute() => light.turnOn();
}
class TurnOffCommand implements Command {
final Light light;
TurnOffCommand(this.light);
@override
void execute() => light.turnOff();
}
class RemoteControl {
Command? _command;
void setCommand(Command command) {
_command = command;
}
void pressButton() {
_command?.execute();
}
}
void main() {
var light = Light();
var remote = RemoteControl();
remote.setCommand(TurnOnCommand(light));
remote.pressButton(); // Licht ist AN
remote.setCommand(TurnOffCommand(light));
remote.pressButton(); // Licht ist AUS
}Die RemoteControl weiß nichts über Lichter – sie führt einfach den Befehl aus, den sie erhält. Diese Entkopplung macht es einfach, neue Befehle hinzuzufügen, ohne den Invoker zu modifizieren. Das Command-Pattern ist ideal für die Implementierung von Undo/Redo-Systemen, Task-Queues oder Makro-Aufzeichnungen, bei denen Aktionen als First-Class-Objekte behandelt werden müssen.
Aufgabe
EinfachLass uns ein Texteditor-Befehlssystem mit dem Command-Pattern bauen! Du wirst ein System erstellen, das Textoperationen als Befehlsobjekte kapselt, sodass ein Editor verschiedene Aktionen ausführen kann, ohne die Details jeder Operation zu kennen. Dies ist ein klassischer Anwendungsfall für das Command-Pattern – stell es dir wie ein Undo/Redo-System vor, bei dem jede Aktion als Objekt gespeichert wird.
Du wirst deinen Code in zwei Dateien organisieren:
commands.dart: Erstelle hier dein Befehlssystem. Beginne mit einer abstrakten KlasseCommand, die eineexecute()-Methode deklariert. Erstelle dann eineTextDocument-Klasse (den Empfänger), die einString content-Feld (anfänglich leer) hält und drei Methoden besitzt:append(String text)– fügt Text am Ende des Inhalts hinzuclear()– setzt den Inhalt auf einen leeren Stringshow()– gibtDocument: [content]aus
TextDocumentarbeiten:AppendCommand– nimmt ein Dokument und Text in seinem Konstruktor entgegen und hängt den Text an, wenn er ausgeführt wirdClearCommand– nimmt ein Dokument in seinem Konstruktor entgegen und leert es, wenn er ausgeführt wirdShowCommand– nimmt ein Dokument in seinem Konstruktor entgegen und zeigt dessen Inhalt an, wenn er ausgeführt wird
TextEditor-Klasse (den Aufrufer), die eine Liste von Befehlen speichert. Sie sollte eineaddCommand(Command command)-Methode haben, um Befehle in die Warteschlange zu stellen, und einerunAll()-Methode, die alle in der Warteschlange befindlichen Befehle der Reihe nach ausführt.main.dart: Importiere deine Befehlsdatei und demonstriere das Command-Pattern. Erstelle einTextDocumentund einenTextEditor. Stelle die folgenden Befehle nacheinander in die Warteschlange: hängeHelloan, hängeWorldan, zeige das Dokument an, hänge!an, zeige es erneut an, leere das Dokument und zeige es ein letztes Mal an. Führe dann alle in der Warteschlange befindlichen Befehle aus.
Beachte, dass der TextEditor nichts über das Anhängen, Leeren oder Anzeigen weiß – er führt einfach die Befehle aus, die ihm gegeben werden. Diese Entkopplung ist der Kern des Command-Patterns!
Erwartete Ausgabe:
Document: Hello World
Document: Hello World!
Document: Probier es selbst
import 'commands.dart';
void main() {
// TODO: Erstelle eine TextDocument-Instanz
// TODO: Erstelle eine TextEditor-Instanz
// TODO: Reihe die folgenden Befehle in dieser Reihenfolge ein:
// 1. Hänge "Hello" an
// 2. Hänge " World" an
// 3. Zeige das Dokument an
// 4. Hänge "!" an
// 5. Zeige das Dokument erneut an
// 6. Leere das Dokument
// 7. Zeige das Dokument ein letztes Mal an
// TODO: Führe alle eingereihten Befehle 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 this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor12Asynchrone OOP
Futures & async/awaitStreams GrundlagenStream ControllerAsynchrone KonstruktorenAsync in KlassenmethodenZusammenfassung – Data Fetcher15Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterComposite-MusterRepository-Muster