Command-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy. Lektion 94 von 110.
Das Command-Muster kapselt eine Anfrage als Objekt und ermöglicht es dir, Clients mit verschiedenen Anfragen zu parametrisieren, Vorgänge in eine Warteschlange zu stellen oder eine Rückgängig-Funktion zu unterstützen. Anstatt eine Methode direkt aufzurufen, verpackst du die Aktion in ein Befehlsobjekt, das gespeichert, weitergegeben oder später ausgeführt werden kann.
Das Muster umfasst drei zentrale Komponenten: eine Command-Schnittstelle mit einer execute-Methode, Concrete Commands, die bestimmte Aktionen implementieren, und einen Receiver, der die eigentliche Arbeit ausführt. 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 jeden Befehl aus, der ihr übergeben wird. Diese Entkopplung erleichtert das Hinzufügen neuer Befehle, ohne den Aufrufer ändern zu müssen. Das Command-Muster eignet sich ideal für die Implementierung von Rückgängig-/Wiederholen-Systemen, Aufgabenwarteschlangen oder Makroaufzeichnungen, bei denen Aktionen als Objekte erster Klasse behandelt werden müssen.
Aufgabe
EinfachErstelle ein Befehlsystem für einen Texteditor mit dem Command-Pattern! Du erstellst ein System, das Textoperationen als Befehlsobjekte kapselt, sodass ein Editor verschiedene Aktionen ausführen kann, ohne die Details der einzelnen Operationen zu kennen. Dies ist ein klassischer Anwendungsfall für das Command-Pattern – stell es dir wie ein Rückgängig-/Wiederholen-System vor, bei dem jede Aktion als Objekt gespeichert wird.
Du organisierst deinen Code in zwei Dateien:
commands.dart: Erstelle hier dein Befehlsystem. Beginne mit einer abstrakten KlasseCommand, die eineexecute()-Methode deklariert. Erstelle dann eineTextDocument-Klasse (den Empfänger), die einString content-Feld (anfangs leer) enthält und drei Methoden besitzt:append(String text)– fügt Text am Ende des Inhalts hinzuclear()– setzt den Inhalt auf eine leere Zeichenketteshow()– gibtDocument: [content]aus
TextDocumentarbeiten:AppendCommand– erhält in seinem Konstruktor ein Dokument und Text und hängt den Text bei der Ausführung anClearCommand– erhält in seinem Konstruktor ein Dokument und leert es bei der AusführungShowCommand– erhält in seinem Konstruktor ein Dokument und zeigt dessen Inhalt bei der Ausführung an
TextEditor-Klasse (den Aufrufer), die eine Liste von Befehlen speichert. Sie sollte eineaddCommand(Command command)-Methode zum Einreihen von Befehlen und einerunAll()-Methode besitzen, die alle eingereihten Befehle der Reihe nach ausführt.main.dart: Importiere deine Befehlsdatei und demonstriere das Command-Pattern. Erstelle einTextDocumentund einenTextEditor. Reiht die folgenden Befehle in dieser Reihenfolge ein:Helloanhängen,Worldanhängen, das Dokument anzeigen,!anhängen, es erneut anzeigen, das Dokument leeren und es ein letztes Mal anzeigen. Führe dann alle eingereihten Befehle aus.
Beachte, dass der TextEditor nichts über das Anhängen, Leeren oder Anzeigen weiß – er führt einfach die Befehle aus, die ihm übergeben werden. Diese Entkopplung ist das Wesentliche 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 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