creational
Command
Encapsulate a request as an object for queuing, logging, and undoable operations.
Normally you call a method and it runs immediately. Sometimes that is not enough.
You may need to queue the action for later, undo it, log it, or retry it after a failure. You cannot do any of that with a plain method call, because once it runs there is nothing left to hold.
Command turns the action into an object. Now it can be stored in a list, passed around, saved, and executed whenever you choose.
A restaurant order slip — the waiter hands the slip to the kitchen. Slips can be queued or cancelled.
Key Concepts
1
Each command implements a small interface, usually with an execute() method.
execute()
2
The command holds everything it needs: which object to act on and the arguments. It is self-contained, so whoever runs it needs to know nothing about it.
3
Because commands are objects, you can keep them in a queue and run them later, or in a list and replay them.
4
For undo, add an undo() method and keep a history stack. Store the values needed to reverse the change, not a reference to state that may have moved on since.
undo()
When to use it
- Text editors (undo/redo)
- Task queues and job schedulers
- Transaction management and rollback
- Macro recording
Watch out for
- Every action becomes a class, so the count grows quickly for what may be one-line operations
- Undo is only as good as the state you captured — commands that store a reference to mutable state rather than a snapshot will undo to the wrong value
- Queued or persisted commands must stay deserialisable as the code evolves; a renamed field can make yesterday's queued command unreplayable
java
public interface Command {
void execute();
void undo();
}
public class InsertTextCommand implements Command {
private final TextEditor editor;
private final String text;
private final int pos;
public InsertTextCommand(TextEditor e, String t, int p) {
this.editor = e; this.text = t; this.pos = p;
}
public void execute() { editor.insert(text, pos); }
public void undo() { editor.delete(pos, text.length()); }
}
public class History {
private final Deque<Command> stack = new ArrayDeque<>();
public void execute(Command c) { c.execute(); stack.push(c); }
public void undo() { if (!stack.isEmpty()) stack.pop().undo(); }
}