Menu
Coddy logo textTech

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: ein Command-Interface 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();  // Light is ON

  remote.setCommand(TurnOffCommand(light));
  remote.pressButton();  // Light is OFF
}

Die RemoteControl weiß nichts über Lichter – sie führt einfach jeden Befehl aus, der ihr gegeben wird. 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.

challenge icon

Aufgabe

Einfach

Lass uns ein Befehlssystem für einen Texteditor mit dem Command-Pattern erstellen! Du wirst ein System entwickeln, 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 Klasse Command, die eine execute()-Methode deklariert. Erstelle dann eine TextDocument-Klasse (den Empfänger), die ein String content-Feld (anfänglich leer) enthält und drei Methoden besitzt:
    • append(String text) – fügt Text am Ende des Inhalts hinzu
    • clear() – setzt den Inhalt auf einen leeren String
    • show() – gibt Document: [content] aus
    Implementiere nun drei konkrete Befehle, die mit einem TextDocument arbeiten:
    • AppendCommand – nimmt ein Dokument und Text in seinem Konstruktor entgegen und hängt den Text beim Ausführen an
    • ClearCommand – nimmt ein Dokument in seinem Konstruktor entgegen und leert es beim Ausführen
    • ShowCommand – nimmt ein Dokument in seinem Konstruktor entgegen und zeigt dessen Inhalt beim Ausführen an
    Erstelle schließlich eine TextEditor-Klasse (den Aufrufer), die eine Liste von Befehlen speichert. Sie sollte eine addCommand(Command command)-Methode zum Einreihen von Befehlen und eine runAll()-Methode haben, die alle eingereihten Befehle der Reihe nach ausführt.
  • main.dart: Importiere deine Befehlsdatei und demonstriere das Command-Pattern. Erstelle ein TextDocument und einen TextEditor. Reihe die folgenden Befehle nacheinander ein: hänge Hello an, hänge World an, 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 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 gegeben werden. Diese Entkopplung ist der Kern des Command-Patterns!

Erwartete Ausgabe:

Document: Hello World
Document: Hello World!
Document: 

Spickzettel

Das Command-Pattern (Befehlsmuster) kapselt eine Anfrage als Objekt und ermöglicht es Ihnen, Clients mit verschiedenen Anfragen zu parametrisieren, Operationen in eine Warteschlange zu stellen oder eine Rückgängig-Funktionalität zu unterstützen.

Das Muster umfasst drei Schlüsselkomponenten:

  • Command-Schnittstelle mit einer execute-Methode
  • Konkrete Befehle (Concrete Commands), die spezifische Aktionen implementieren
  • Empfänger (Receiver), der die eigentliche Arbeit verrichtet
  • Aufrufer (Invoker) (optional), der Befehle auslöst, ohne zu wissen, was sie tun

Grundstruktur:

abstract class Command {
  void execute();
}

class Receiver {
  void action() => print('Action performed');
}

class ConcreteCommand implements Command {
  final Receiver receiver;
  ConcreteCommand(this.receiver);

  @override
  void execute() => receiver.action();
}

class Invoker {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void trigger() {
    _command?.execute();
  }
}

Beispiel mit einem Lichtsteuerungssystem:

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 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();  // Light is ON
}

Der Aufrufer weiß nichts über den Empfänger – er führt einfach den Befehl aus, der ihm gegeben wurde. Diese Entkopplung macht es einfach, neue Befehle hinzuzufügen, ohne den Aufrufer zu ändern.

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
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung