Command โ Behavioral design pattern with UML diagrams, Java implementation, and real-world examples.
Behavioral
Command Pattern
Encapsulate a request as an object to support undo, queuing, and logging. The backbone of undo/redo systems, macro recorders, job queues, and transactional operations.
You are building a text editor. The toolbar has buttons for Bold, Italic, Underline, and Delete. Each button directly calls a method on the Document. Now you need to add undo/redo, macro recording, and keyboard shortcuts that trigger the same operations. The naive approach: every UI element hard-codes the operation logic.
Naive approach — operations hard-wired into UI
// โ Each button knows exactly what to do โ no undo, no queuingclassBoldButton {
privateDocument doc;
public voidonClick() {
doc.applyBold(doc.getSelection()); // โ hard-coded action// How to undo this? Need to store previous state here?// How to replay this as a macro? Duplicate the logic?
}
}
classDeleteButton {
privateDocument doc;
public voidonClick() {
doc.delete(doc.getSelection()); // โ different hard-coded action// Same undo problem. Same macro problem.
}
}
// Keyboard shortcut Ctrl+B? Duplicate BoldButton logic. Menu item? Duplicate again.
What goes wrong:
No undo/redo — the operation is executed and forgotten; there’s no record of what happened or how to reverse it
Duplicated logic — the same operation exists in the button, the keyboard shortcut handler, the menu item, and the macro player
Tight coupling — every UI element depends directly on Document’s API; changing the document model means editing every button class
No queuing or logging — you can’t serialize operations, schedule them for later, or replay them for debugging
Without Command — UI elements hard-wired to receiver methods
This is the problem Command solves — encapsulate each operation as a self-contained object that knows how to execute() and undo() itself. The invoker (button, shortcut, macro) holds a Command reference and doesn’t know or care what the command does internally.
02
Section Two ยท The Pattern
What Is Command?
Command is a behavioral pattern that encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Instead of a button directly calling document.applyBold(), the button holds a Command object and calls command.execute(). The command object contains all the information needed to perform the action — and to reverse it.
GoF Intent: “Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.” — Gamma, Helm, Johnson, Vlissides (1994)
Analogy — restaurant order slip: When you order food at a restaurant, you don’t walk into the kitchen and cook it yourself. The waiter writes your order on a slip (the command object). The slip contains everything needed: dish name, modifications, table number. The waiter (invoker) doesn’t know how to cook — they just pass the slip to the kitchen (receiver). The kitchen executes the order. The slip can be queued (busy night), logged (receipt), or cancelled (you change your mind). The waiter doesn’t know or care about the recipe — they only know that every slip has an “execute” (prepare the dish). This is Command: the request is an object that can be stored, passed around, queued, undone, and replayed.
Key insight: Command turns a verb (an action) into a noun (an object). Once an action is an object, you can do anything with it that you can do with any object: store it in a list (history), serialize it (audit log), pass it to another thread (job queue), or call a reverse method on it (undo). This single transformation — action-as-object — unlocks undo/redo, macro recording, transaction rollback, and task scheduling.
Each operation is a class implementing Command with execute() and undo()
▶
The invoker (button, shortcut, scheduler) doesn’t know what the command does — it just calls execute()
Executed commands are pushed onto a history stack
▶
Undo = pop the last command and call undo(). Redo = push it back and call execute() again
Commands are objects — they can be serialized, queued, batched
▶
Macro recording = store a list of commands. Replay = iterate and execute() each. Transaction log = serialize the list.
Multiple trigger points (button, menu, shortcut) share the same Command instance
▶
Zero duplication. Change the operation logic in one place — all triggers reflect it immediately.
03
Section Three ยท Anatomy
Participants & Structure
Participant
Role
In the Analogy
Command(interface)
Declares execute() and optionally undo(). Every concrete command implements this. The invoker depends only on this interface — never on concrete commands.
The order slip format — every slip has a “prepare” action regardless of the dish.
Concrete Command
Implements execute() by calling specific methods on the Receiver. Stores any state needed for undo() (e.g. previous text, old selection).
A specific order slip: “1ร Margherita, extra basil, table 5.”
Receiver
The object that actually performs the work. The command delegates to it. The receiver doesn’t know about commands — it just exposes domain methods.
The kitchen — knows how to cook every dish but doesn’t know about order slips.
Invoker
Holds a command reference and triggers execute(). Doesn’t know what the command does. May maintain a history stack for undo/redo.
The waiter — collects slips and passes them to the kitchen. Doesn’t cook.
Client
Creates concrete command objects, wires them to receivers, and assigns them to invokers. The only participant that knows the full picture.
The restaurant manager who designs the menu (which dish maps to which recipe).
Command — UML Class Diagram
Command vs. Strategy structure:
Both have an interface with concrete implementations.
The difference is intent: Strategy swaps an algorithm for an interchangeable behaviour.
Command encapsulates a request with full context, enabling undo, queuing, and history.
A Strategy is stateless (“how to sort”); a Command carries state (“what to undo”).
04
Section Four ยท How It Works
The Pattern In Motion
Scenario: A text editor with undo/redo. The user selects text, clicks Bold, then clicks Delete, then presses Ctrl+Z to undo the delete.
The button (invoker) now holds a Command reference. It doesn’t know it’s a BoldCommand — just that it can call execute() and undo().
Step 2 — User clicks Bold: invoker calls command.execute()
▶
BoldCommand.execute() saves the current text as backup, then calls doc.applyBold(selection). The invoker pushes this command onto the history stack.
Step 3 — User clicks Delete: invoker calls deleteCmd.execute()
▶
DeleteCommand.execute() saves the deleted text as backup, then calls doc.delete(selection). Pushed onto history stack (now 2 items).
Step 4 — User presses Ctrl+Z: invoker calls undoLast()
▶
Invoker pops DeleteCommand from the history stack and calls deleteCmd.undo(). The command restores the deleted text from its backup. Document is back to post-bold state.
Step 5 — User presses Ctrl+Z again
▶
Invoker pops BoldCommand and calls boldCmd.undo(). The command restores the original unbolded text. Document is back to the initial state. History stack is empty.
Command — Execute + Undo Sequence
The pattern in pseudocode
// โโ Setup (Client wires everything) โโDocument doc = newDocument("Hello World");
EditorInvoker editor = newEditorInvoker();
// โโ User clicks Bold โโCommand bold = newBoldCommand(doc, 0, 5);
editor.executeCommand(bold);
// Output: Applied bold to "Hello"// โโ User clicks Delete โโCommand del = newDeleteCommand(doc, 6, 11);
editor.executeCommand(del);
// Output: Deleted "World"// โโ User presses Ctrl+Z โโ
editor.undoLast();
// Output: Undo delete โ restored "World"// โโ User presses Ctrl+Z again โโ
editor.undoLast();
// Output: Undo bold โ restored original "Hello"
Behavioral patterns need both static UML and dynamic sequence:
The UML (S03) shows who participates. The sequence diagram above shows who calls whom in what order.
For Command, the key runtime insight is: execute() delegates to the receiver and stores undo state; undo() reverses the action using that stored state.
The invoker orchestrates the history stack but never knows the details.
05
Section Five ยท Java Stdlib
You Already Use This
Command is embedded deeply in Java’s concurrency model and GUI framework. Every time you pass a Runnable to a thread pool, you’re using Command — encapsulating an action as an object that can be queued, scheduled, and executed later.
IN JAVA
Example 1java.lang.Runnable — the simplest Command interface in Java. A single method: run(). You create a Runnable (concrete command), pass it to a Thread or ExecutorService (invoker), and it executes the encapsulated action. The executor doesn’t know what the Runnable does — it just calls run().
Example 2java.util.concurrent.Callable<V> — a Command that returns a result. call() is the execute() method. Submitted to an ExecutorService (invoker), which queues it and returns a Future<V>. The invoker can schedule, cancel, or retry the command without knowing its internals.
Example 3javax.swing.Action — a rich Command interface for Swing GUIs. Extends ActionListener with name, icon, enabled state, and actionPerformed(). A single Action object can be shared by a toolbar button, menu item, and keyboard shortcut — exactly what Command prescribes. Change the action’s enabled state, and all three UI elements update.
Example 4java.util.TimerTask — a scheduled command. Extends Runnable with scheduling metadata. The Timer (invoker) queues TimerTask objects and executes them at specified times. Same pattern: encapsulated action + deferred execution.
Stdlib usage — Runnable as Command
// Runnable = Command interface, ExecutorService = InvokerExecutorService pool = Executors.newFixedThreadPool(4);
// Encapsulate actions as command objectsRunnable sendEmail = () -> System.out.println("Sending email...");
Runnable generatePdf = () -> System.out.println("Generating PDF...");
Runnable resizeImage = () -> System.out.println("Resizing image...");
// Invoker queues and executes โ doesn't know what each command does
pool.submit(sendEmail);
pool.submit(generatePdf);
pool.submit(resizeImage);
pool.shutdown();
Stdlib usage — Swing Action shared across UI elements
// One Action (command) shared by button, menu, and shortcutAction saveAction = newAbstractAction("Save") {
public voidactionPerformed(ActionEvent e) {
document.save(); // receiver
}
};
// Same command object โ three trigger points, zero duplication
toolbar.add(saveAction); // toolbar button
fileMenu.add(saveAction); // menu item
inputMap.put(ctrlS, "save"); // keyboard shortcut
actionMap.put("save", saveAction);
Why Runnable is Command, not Strategy:
A Comparator (Strategy) defines how to compare — it’s stateless and interchangeable.
A Runnable (Command) encapsulates a specific action to execute later — it’s queued, scheduled, or passed to another thread.
The key difference: Commands are created, stored, and potentially undone. Strategies are swapped.
06
Section Six ยท Implementation
Build It Once
Domain: Text Editor Undo/Redo. A TextDocument receiver holds editable content. Commands (BoldCommand, DeleteCommand) encapsulate operations with full undo support. An EditorInvoker maintains command history and provides undo/redo.
Java — Command Pattern Text Editor (core)
// โโ Command interface โโinterfaceCommand {
voidexecute();
voidundo();
Stringdescription();
}
// โโ Invoker with history โโclassEditorInvoker {
private finalDeque<Command> history = newArrayDeque<>();
private finalDeque<Command> redoStack = newArrayDeque<>();
public voidexecute(Command cmd) {
cmd.execute();
history.push(cmd);
redoStack.clear(); // new action invalidates redo
}
public voidundo() {
if (!history.isEmpty()) {
Command cmd = history.pop();
cmd.undo();
redoStack.push(cmd);
}
}
public voidredo() {
if (!redoStack.isEmpty()) {
Command cmd = redoStack.pop();
cmd.execute();
history.push(cmd);
}
}
}
If you undo twice (A โ B โ C becomes A โ B), then execute a new command D, the redo stack (which held C) must be cleared.
Otherwise redo would restore C, which was based on state that no longer exists.
This is how every real editor works — new actions after undo invalidate the redo branch.
07
Section Seven ยท Watch Out
Common Mistakes
Mistake #1 — Forgetting to store undo state before execute: A DeleteCommand calls doc.delete() but never stores what was deleted. When undo() is called, there’s nothing to restore. Fix: Always capture the “before” state inside execute()before mutating the receiver. For delete: save the deleted text. For bold: save the original range. For insert: you just need the position and length to reverse.
✗ Wrong — no backup stored
// โ execute() deletes text but never saves itclassDeleteCommandimplementsCommand {
public voidexecute() {
doc.delete(start, end); // โ text is gone forever
}
public voidundo() {
// โ What do we insert? We didn't save the deleted text!
doc.insert(start, ???);
}
}
✔ Correct — backup before mutate
// โ Save deleted text inside execute(), use it in undo()classDeleteCommandimplementsCommand {
privateString backup;
public voidexecute() {
backup = doc.getText(start, end); // โ save first
doc.delete(start, end); // โ then mutate
}
public voidundo() {
doc.insert(start, backup); // โ restore from backup
}
}
Mistake #2 — Making commands stateless (confusing Command with Strategy):
If your command doesn’t store any state — no receiver reference, no parameters, no backup — it’s not a Command, it’s a Strategy.
A Command must carry the context needed to execute and undo a specific request.
A “BoldCommand” without knowing which range to bold and what was there before is useless for undo.
Mistake #3 — Not clearing the redo stack on new commands:
User undoes action A, then performs a new action B.
If the redo stack still holds A, pressing redo would try to re-execute A on a state it no longer matches — causing corruption.
Fix: Always call redoStack.clear() in execute(). Every real editor does this — once you type after undoing, the undone branch is lost.
Mistake #4 — Coupling the invoker to concrete commands:
If the invoker imports BoldCommand, DeleteCommand, etc., you’ve lost the decoupling benefit.
The invoker should know only the Command interface.
The client (setup code) is responsible for creating concrete commands and assigning them to the invoker.
Fix: Invoker accepts Command; client does button.setCommand(new BoldCommand(doc, range)).
Mistake #5 — Unbounded history stack:
Every executed command stays in memory forever.
In a real editor, 10,000 operations means 10,000 command objects — each potentially holding a backup string.
Fix: Cap history size (e.g. 100 undos). Use circular buffer or Deque with max capacity. For large state, consider Memento pattern for efficient snapshots.
08
Section Eight ยท Decision Guide
When To Use Command
Use Command When
You need undo/redo — each operation must be reversible, and the system must track execution history
You want to queue, schedule, or log operations — requests must exist as objects that can be stored and replayed
Multiple trigger points (button, menu, shortcut, API) must execute the same operation without duplicating logic
You need macro recording — record a sequence of commands and replay them as a batch
You want to decouple the object that invokes an operation from the object that performs it — the invoker should not know about the receiver
You need transactional behaviour — execute a batch of commands, and roll them all back if one fails
Avoid Command When
The operation is simple and one-shot — no undo, no queuing, no history needed; a direct method call is simpler
You only need to swap algorithms at runtime without tracking history — use Strategy instead
The action has no meaningful undo — sending an email or charging a credit card can’t be reversed by calling undo(); consider Saga or compensating transactions instead
There is only one invoker and one receiver with a stable API — the indirection of Command adds complexity without benefit
Command vs. Confused Patterns
Pattern
Encapsulates
Carries State?
When to pick it
Command ← this
A request (action + receiver + params)
Yes — backup for undo, params for execute
Undo/redo, queuing, logging, macros
Strategy
An algorithm (how to do something)
No — stateless, interchangeable
Swap behaviour at runtime without history
Observer
A notification (one-to-many broadcast)
No — fire and forget
Multiple listeners react to state change
Memento
An object’s internal state snapshot
Yes — full state copy
Undo by restoring full state (not action-based)
Chain of Responsibility
A request passed along a chain
No — request handled by one handler
Request needs to find the right handler
Decision Flowchart
09
Section Nine ยท Practice
Problems To Solve
Command problems test whether you can encapsulate operations as objects, implement reversible execution, manage history stacks, and handle edge cases like macro batching and transactional rollback.
Difficulty
Problem
Key Insight
Easy
Remote Control Build a universal remote control with 7 slots. Each slot can be assigned a Command (e.g. turn on light, start fan, open garage). The remote has an “undo” button that reverses the last action. Commands can be reassigned at runtime.
Tests the basic Command structure: interface with execute()/undo(), invoker with slots, and receiver objects. The key part: the remote knows nothing about lights or fans — it only calls command.execute(). Undo stores the last executed command and calls undo() on it.
Medium
Database Transaction with Rollback Build a TransactionManager that executes a batch of Commands (INSERT, UPDATE, DELETE). If any command throws an exception, the entire batch is rolled back by calling undo() on all previously executed commands in reverse order.
Tests transactional Command usage. The TransactionManager (invoker) executes commands sequentially, storing each in a “done” list. On failure, it iterates the done list in reverse calling undo(). Each command stores enough state to reverse itself (e.g. DELETE saves the row, INSERT saves the generated ID for removal).
Medium
Macro Recorder Build a MacroCommand that records a sequence of user actions and replays them. The user presses “Record”, performs actions (bold, insert, delete), presses “Stop”, then can “Play” the macro on any document. The macro itself supports undo() (undoes all actions in reverse).
Tests composite Command (a command that holds a list of commands). MacroCommand.execute() iterates the list and calls execute() on each. MacroCommand.undo() iterates in reverse and calls undo(). The macro is itself a Command — it can be stored in the history stack like any other command.
Hard
Collaborative Editor with Operational Transform Two users edit the same document simultaneously. Each keystroke is a Command sent to a server. The server must transform and reorder concurrent commands so that both users converge to the same document state. Implement transform(cmd1, cmd2) for insert-insert conflicts.
Tests advanced Command usage in distributed systems. Each command carries position + content. When two inserts arrive concurrently, the server transforms the second command’s position based on the first command’s effect. This is the foundation of Google Docs — commands (operations) as first-class objects enable transformation, reordering, and conflict resolution.
Interview Tip:
When asked about Command, the interviewer wants to see: (1) a Command interface with execute() and undo(); (2) concrete commands that store receiver + params + undo state; (3) an invoker that maintains a history stack; (4) clear separation — invoker depends only on the Command interface.
Stand-out answers mention: the difference from Strategy (stateless algorithm vs. stateful request), how Runnable/Callable are Commands in the JDK, how to handle the redo stack (clear on new execute), and how Command + Memento compose for complex undo (Command for action-based undo, Memento for full-state snapshots when actions are too complex to reverse incrementally).