Design Patterns - Behavioral
Command Pattern
Encapsulate a request as an object, allowing you to parameterize clients, queue requests, and support undoable operations.
12 min readEssential
1What is the Command Pattern?
Real-World Analogy
Restaurant Order
Customer tells waiter their order. Waiter writes it on a slip (Command object) and places it in the kitchen queue. The chef (Receiver) executes the order. The slip can be cancelled or modified before execution.
Remote Control
Each button on a remote is a Command. Pressing "On" creates a TurnOnCommand. The TV (Receiver) executes it. The remote does not know how to turn on the TV - it just invokes the command.
Command Pattern turns a request into a stand-alone object containing all information about the request. This allows you to: parameterize methods with different requests, delay or queue execution, and implement undo/redo operations.
Command Pattern Structure
Client
Creates command
→
Command
execute(), undo()
→
Invoker
Triggers command
→
Receiver
Does actual work
2Why Use Command Pattern
✅ Undo/Redo Support
Store executed commands in history. Call undo() to reverse operations.
✅ Request Queuing
Queue commands for delayed execution, batch processing, or scheduling.
✅ Logging and Auditing
Log all commands for debugging, auditing, or replaying operations.
✅ Decoupling
Invoker is decoupled from receiver. Can swap commands without changing invoker.
3Implementation
Text Editor with Undo/Redo support using Command pattern:
Text Editor with Undo/Redo
// ========== Command Interface ==========
interface Command {
void execute();
void undo();
}
// ========== Receiver - The actual text editor ==========
class TextEditor {
private StringBuilder text = new StringBuilder();
public void write(String content) {
text.append(content);
}
public void deleteLast(int length) {
int start = text.length() - length;
if (start >= 0) {
text.delete(start, text.length());
}
}
public String getText() {
return text.toString();
}
}
// ========== Concrete Commands ==========
class WriteCommand implements Command {
private TextEditor editor;
private String content;
public WriteCommand(TextEditor editor, String content) {
this.editor = editor;
this.content = content;
}
@Override
public void execute() {
editor.write(content);
}
@Override
public void undo() {
editor.deleteLast(content.length());
}
}
class DeleteCommand implements Command {
private TextEditor editor;
private String deletedContent;
private int length;
public DeleteCommand(TextEditor editor, int length) {
this.editor = editor;
this.length = length;
}
@Override
public void execute() {
String text = editor.getText();
int start = Math.max(0, text.length() - length);
deletedContent = text.substring(start);
editor.deleteLast(length);
}
@Override
public void undo() {
editor.write(deletedContent);
}
}
// ========== Invoker - Manages command history ==========
class EditorInvoker {
private Stack<Command> history = new Stack<>();
private Stack<Command> redoStack = new Stack<>();
public void execute(Command command) {
command.execute();
history.push(command);
redoStack.clear(); // Clear redo after new command
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
redoStack.push(command);
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command command = redoStack.pop();
command.execute();
history.push(command);
}
}
}
// ========== Usage ==========
public class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
EditorInvoker invoker = new EditorInvoker();
// Write some text
invoker.execute(new WriteCommand(editor, "Hello"));
System.out.println(editor.getText()); // Hello
invoker.execute(new WriteCommand(editor, " World"));
System.out.println(editor.getText()); // Hello World
// Undo last write
invoker.undo();
System.out.println(editor.getText()); // Hello
// Redo
invoker.redo();
System.out.println(editor.getText()); // Hello World
// Delete 5 characters
invoker.execute(new DeleteCommand(editor, 5));
System.out.println(editor.getText()); // Hello
// Undo delete
invoker.undo();
System.out.println(editor.getText()); // Hello World
}
}Output:
Hello Hello World Hello Hello World Hello Hello World
4Real-World Examples
GUI ActionsMenu items, toolbar buttons as commands
TransactionsDatabase transactions with commit/rollback
Task QueuesJob schedulers, message queues
MacrosRecord and playback sequences of commands
ReduxActions are command objects dispatched to store
5Interview Questions
What is the Command pattern and when would you use it?
Command encapsulates a request as an object with all its parameters. Use it when: 1) You need undo/redo functionality, 2) You want to queue or log requests, 3) You need to parameterize objects with operations, 4) You want to decouple invoker from receiver.
How does Command pattern enable undo/redo?
Each Command stores the information needed to reverse its operation. Commands are pushed to a history stack. To undo, pop the last command and call its undo() method, which reverses the operation. For redo, maintain a separate redo stack.
What are the participants in Command pattern?
1) Command: Interface with execute() and optionally undo(). 2) ConcreteCommand: Implements Command, binds receiver to action. 3) Invoker: Asks command to execute. 4) Receiver: Knows how to perform the actual work. 5) Client: Creates commands and sets their receivers.
6Key Takeaways
1Command encapsulates request as object with execute() and undo().
2Enables undo/redo by storing command history.
3Decouples invoker from receiver.
4Supports queuing, logging, and scheduling of requests.
5Real examples: GUI actions, transactions, Redux actions.
6Each command stores all info needed to execute and undo.