Command.java
package commands.schlecht;
public interface Command {
String type();
}
Command ist ein Verhaltensmuster, das Anfragen als Objekte kapselt.
Command.java
package commands.schlecht;
public interface Command {
String type();
}
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???
}
}
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.
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.
public interface Command {
void execute();
void undo();
}
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());
}
}
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);
}
}
}
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();
}
}
GUI-Komponenten: Buttons, Menüs, Toolbar-Aktionen.
Undo / Redo: Aktionen rückgängig machen.
Logging: Operationen speichern und erneut abspielen.
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.
👍Entkoppelt Sender und Empfänger einer Anfrage.
👍Mehrere Software-Design-Prinzipen erfüllt.
👍Unterstützt Undo/Redo.
👎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.
Es soll eine einfache Fernbedienung implementiert werden, die Folgendes kann:
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
Fragen?