Command Pattern
IntermediateEncapsulates a request as an object, enabling parameterization, queuing, logging, and undo/redo of operations.
Overview
The Command pattern turns a method call into a standalone object. The Command object captures the action, its arguments, and the receiver. The Invoker stores and executes commands without knowing what they do. This enables: queuing (job queue, task scheduler), logging (audit trail of executed commands), and undo/redo (maintaining a history stack of reversible commands). The Command object is the unit of work — it is serializable, transmittable, and replayable. Used in GUI frameworks (menu actions, button clicks), transactional job systems, and macro recording.
Command with Undo/Redo
Each Command has execute() and undo() methods. The Invoker maintains two stacks — history (for undo) and undone (for redo). Executing a command pushes to history; undoing pushes to undone.
import java.util.ArrayDeque;
import java.util.Deque;
// Command interface
public interface Command {
void execute();
void undo();
String description();
}
// Receiver
public class TextEditor {
private final StringBuilder text = new StringBuilder();
public void insertText(int position, String s) {
text.insert(position, s);
}
public void deleteText(int position, int length) {
text.delete(position, position + length);
}
public String getText() { return text.toString(); }
}
// Concrete Command — insert
public class InsertCommand implements Command {
private final TextEditor editor;
private final int position;
private final String text;
public InsertCommand(TextEditor editor, int position, String text) {
this.editor = editor;
this.position = position;
this.text = text;
}
@Override public void execute() { editor.insertText(position, text); }
@Override public void undo() { editor.deleteText(position, text.length()); }
@Override public String description() { return "Insert '" + text + "' at " + position; }
}
// Concrete Command — delete
public class DeleteCommand implements Command {
private final TextEditor editor;
private final int position;
private final int length;
private String deleted; // saved for undo
public DeleteCommand(TextEditor editor, int position, int length) {
this.editor = editor;
this.position = position;
this.length = length;
}
@Override
public void execute() {
deleted = editor.getText().substring(position, position + length);
editor.deleteText(position, length);
}
@Override public void undo() { editor.insertText(position, deleted); }
@Override public String description() { return "Delete " + length + " chars at " + position; }
}
// Invoker — manages history
public class CommandInvoker {
private final Deque<Command> history = new ArrayDeque<>();
private final Deque<Command> undone = new ArrayDeque<>();
public void execute(Command cmd) {
cmd.execute();
history.push(cmd);
undone.clear(); // new command clears redo stack
System.out.println("Executed: " + cmd.description());
}
public void undo() {
if (history.isEmpty()) { System.out.println("Nothing to undo"); return; }
Command cmd = history.pop();
cmd.undo();
undone.push(cmd);
System.out.println("Undone: " + cmd.description());
}
public void redo() {
if (undone.isEmpty()) { System.out.println("Nothing to redo"); return; }
Command cmd = undone.pop();
cmd.execute();
history.push(cmd);
System.out.println("Redone: " + cmd.description());
}
}
// Usage
TextEditor editor = new TextEditor();
CommandInvoker ctrl = new CommandInvoker();
ctrl.execute(new InsertCommand(editor, 0, "Hello"));
ctrl.execute(new InsertCommand(editor, 5, " World"));
System.out.println(editor.getText()); // Hello World
ctrl.undo();
System.out.println(editor.getText()); // Hello
ctrl.redo();
System.out.println(editor.getText()); // Hello WorldKey Points to Remember
- 1Command encapsulates a request as an object — enables queuing, logging, and undo/redo.
- 2execute() performs the action; undo() reverses it. The deleted/old state must be saved in execute().
- 3Invoker knows nothing about the command receiver — it only calls execute()/undo().
- 4Command pattern is used in Java Swing (AbstractAction), Spring Batch (Job/Step), and transactional outbox.
- 5A MacroCommand is a Composite of Commands — execute() calls execute() on each child.
Interview Questions
Sign in to ask AriaHow would you implement undo/redo in a drawing application using Command pattern?
What is the difference between Command and Strategy patterns?
How would you persist a command queue to survive application restarts?
What is a MacroCommand and how does it relate to the Composite pattern?
Ask Aria about Command Pattern
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.