Design pattern

Command

(Action, Transaction)

Was ist das Command Pattern?

Was ist das Command Pattern?

Command ist ein Verhaltensmuster, das Anfragen als Objekte kapselt.

Schlechter Ansatz

Schlechter Ansatz

Command.java


package commands.schlecht;

public interface Command {
    String type();
}
							

Schlechter Ansatz

CommandExecuter.java


package commands.schlecht;

public class CommandExecuter {

        public void executeCommand(Command command) {
            System.out.println("Executing command: " + command.type());
            switch (command.type()) {
                case "DELETE_TEXT":
                    System.out.println("Deleting text...");
                    break;
                case "INSERT_TEXT":
                    System.out.println("Inserting text...");
                    break;
							 // Immer weiter....
                default:
                    System.out.println("Unknown command type: " + command.type());
            }
        }

        public void undoCommand(Command command) {
            System.out.println("Undoing command: " + command.type());
			// Wie war der State???
        }


}
							

Grundidee (Prinzip)

Grundidee (Prinzip)

Trennung: Aufrufer und Ausführer einer Aktion werden entkoppelt.

Kapselung: Methode, Parameter und Empfänger werden in einem Objekt gespeichert.

Flexibilität: Aktionen können übergeben, verzögert oder rückgängig gemacht werden.

Grundidee (Prinzip)

Kernkomponenten

Kernkomponenten

Command – definiert die Schnittstelle, meist execute().

Concrete Command – konkrete Implementierung einer Aktion.

Invoker – löst die Command aus.

Receiver – führt die eigentliche Geschäftslogik aus.

Client – erstellt und verbindet alle Objekte.

Restaurant-Beispiel

Restaurant-Beispiel

Funktionsweise

Funktionsweise

  1. Der Client erstellt den Receiver
  2. Der Client erstellt ein Concrete Command mit dem Receiver als Parameter
  3. Der Client übergibt den Command an den Invoker
  4. Der Invoker ruft execute() auf
  5. Das Command delegiert die Arbeit an den Receiver

Korrekter Ansatz

Korrekter Ansatz

Korrekter Ansatz


public interface Command {
    void execute();
    void undo();
}
				

Korrekter Ansatz


public class InsertTextCommand implements Command {
    private final TextEditor editor;
    private final String text;
    private final int position;

    public InsertTextCommand(TextEditor editor, String text, int position) {
        this.editor = editor;
        this.text = text;
        this.position = position;
    }

    @Override
    public void execute() {
        editor.insert(position, text);
    }

    @Override
    public void undo() {
        editor.delete(position, text.length());
    }
}
				

Korrekter Ansatz


public class CommandManager {
    private final Deque'Command' undoStack = new ArrayDeque''();
    private final Deque'Command' redoStack = new ArrayDeque''();

    public void executeCommand(Command command) {
        command.execute();
        undoStack.push(command);
        redoStack.clear();
    }

    public void undo() {
        if (!undoStack.isEmpty()) {
            Command command = undoStack.pop();
            command.undo();
            redoStack.push(command);
        }
    }

    public void redo() {
        if (!redoStack.isEmpty()) {
            Command command = redoStack.pop();
            command.execute();
            undoStack.push(command);
        }
    }
}
				

Korrekter Ansatz


public class EditorInvoker {
    private final CommandManager commandManager;
    private final TextEditor editor;

    public EditorInvoker(CommandManager commandManager, TextEditor editor) {
        this.commandManager = commandManager;
        this.editor = editor;
    }

    public void onInsertButton(String text, int position) {
        Command command = new InsertTextCommand(editor, text, position);
        commandManager.executeCommand(command);
    }

    public void onUndoButton() {
        commandManager.undo();
    }
}
				

Verwendungsbereich

Verwendungsbereich

GUI-Komponenten: Buttons, Menüs, Toolbar-Aktionen.

Undo / Redo: Aktionen rückgängig machen.

Logging: Operationen speichern und erneut abspielen.

Minecraft

Minecraft

Bezug zu SOLID-Prinzipien

S (Single Responsibility):
Jede Klasse hat eine klare Verantwortung — invoker, receiver, command, etc.

O (Open/ Closed):
Neue Befehle können hinzugefügt werden, ohne bestehende Klassen zu verändern.

D (Dependency Inversion):
Aufrufer basieren auf Abstraktionen (Befehl- Schnittstelle) anstatt auf konkreten Klassen.

Vorteile / Nachteile

Vorteile

👍Entkoppelt Sender und Empfänger einer Anfrage.

👍Mehrere Software-Design-Prinzipen erfüllt.

👍Unterstützt Undo/Redo.

Nachteile

👎Es entstehen oft viele kleine Klassen.

👎Für einfache Projekte kann das Pattern zu komplex sein.

👎Undo erfordert sauberes Zustandsmanagement.

👎Große Command-Historien können Speicher verbrauchen.

👎Nicht jede Aktion mit Seiteneffekten ist leicht rückgängig zu machen.

Code-Aufgabe

Es soll eine einfache Fernbedienung implementiert werden, die Folgendes kann:

  1. eine Lampe einschalten
  2. eine Lampe ausschalten
  3. einen Fernseher einschalten
  4. einen Fernseher ausschalten

Wichtige Anforderung: Die Fernbedienung darf nicht wissen, welches Gerät sie steuert. Sie drückt nur Knöpfe.

Zusätzlich: Neue Geräte sollen hinzugefügt werden können, ohne den Code der Fernbedienung zu ändern

Vielen Dank!

Fragen?