Overview
The Command pattern turns a request into an object that contains everything needed to perform it: the action, the receiver, and the parameters. Because a command is an object, it can be queued, logged, scheduled, retried, sent over a network, and undone.
The invoker (a button, a scheduler, a remote control) calls command.execute() without knowing what it does. Commands with undo() let editors, spreadsheets, and games support undo/redo stacks. Job queues and event-sourced systems are large-scale versions of the same idea.
The waiter writes the order on a ticket (command) and places it on the rail. The cook (receiver) executes it later. The ticket can be queued, reordered, or cancelled without the waiter knowing how to cook.
When to use it
- Undo/redo is required.
- Operations must be queued, scheduled, or retried.
- UI actions (buttons, shortcuts, menus) should share the same operations.
- Macro recording or audit logs of actions.
Where it shows up in interviews
Recognize it when: users can reverse actions.
- Design a text editor
- Design a drawing app
- Design a spreadsheet
Recognize it when: actions executed later or remotely.
- Design a task scheduler
- Design a smart home remote
- Design a job queue
Where it is used in real software
VS Code, Photoshop, and Figma record operations as commands with inverse actions.
Runnable tasks are commands submitted to a thread pool for later execution.
Actions are serializable command-like objects dispatched to reducers, enabling time-travel debugging.
Key terms
- Command
- Object with execute() (and often undo()).
- Receiver
- The object that does the actual work.
- Invoker
- Triggers commands without knowing details.
- Client
- Creates commands and binds them to receivers.
- Macro command
- A command composed of other commands.
How it works, step by step
- 1Define the Command interface
execute() and undo().
- 2Implement concrete commands
Each stores its receiver and parameters.
- 3Store undo information
Previous state or inverse parameters.
- 4Invoker executes and records history
Push to an undo stack.
- 5Undo pops and reverses
Redo re-executes from a redo stack.
STEP 1Three commands execute; each is pushed onto the undo stack.
Text editor history
Document starts empty
| Action | Document | Undo stack | Redo stack |
|---|---|---|---|
| Insert 'Hello' | Hello | [Insert] | [] |
| Insert ' World' | Hello World | [Insert, Insert] | [] |
| Undo | Hello | [Insert] | [Insert ' World'] |
| Redo | Hello World | [Insert, Insert] | [] |
| Delete 5 chars, then undo | Hello World | [Insert, Insert] | [Delete] |
NOWAction: Insert 'Hello' | Document: Hello | Undo stack: [Insert] | Redo stack: []
Each command knows how to reverse itself, so history management is generic.
Implementation
interface Command { execute(): void; undo(): void } class TextDocument { content = ""; } class InsertText implements Command { constructor(private doc: TextDocument, private text: string, private at: number) {} execute() { this.doc.content = this.doc.content.slice(0, this.at) + this.text + this.doc.content.slice(this.at); } undo() { this.doc.content = this.doc.content.slice(0, this.at) + this.doc.content.slice(this.at + this.text.length); }} class DeleteText implements Command { private removed = ""; constructor(private doc: TextDocument, private at: number, private length: number) {} execute() { this.removed = this.doc.content.slice(this.at, this.at + this.length); this.doc.content = this.doc.content.slice(0, this.at) + this.doc.content.slice(this.at + this.length); } undo() { this.doc.content = this.doc.content.slice(0, this.at) + this.removed + this.doc.content.slice(this.at); }} class Editor { private undoStack: Command[] = []; private redoStack: Command[] = []; run(cmd: Command) { cmd.execute(); this.undoStack.push(cmd); this.redoStack = []; } undo() { const c = this.undoStack.pop(); if (c) { c.undo(); this.redoStack.push(c); } } redo() { const c = this.redoStack.pop(); if (c) { c.execute(); this.undoStack.push(c); } }} const doc = new TextDocument();const editor = new Editor();editor.run(new InsertText(doc, "Hello", 0));editor.run(new InsertText(doc, " World", 5));editor.undo(); // "Hello"editor.redo(); // "Hello World"Complexity and performance
Plus O(1) stack ops.
Cap history length.
Trade-offs
A class per action adds code; lambdas can replace simple commands without undo.
Storing previous state is simple but memory heavy; storing inverse operations is compact but harder.
Variants and related techniques
A composite of commands executed and undone as one.
Serialized commands processed by workers.
Command stores how to reverse; Memento stores snapshots.
Common mistakes
- Not clearing redo after a new command.
Fix: New actions invalidate the redo branch.
- Commands that depend on mutable external state at undo time.
Fix: Capture needed data at execute time.
Interview questions
How would you implement undo/redo in a text editor?
Model each edit as a Command with execute() and undo(); keep an undo stack and a redo stack. Undo pops from undo, reverses, and pushes to redo; any new command clears redo.
Command vs Strategy?
A Strategy is how to do something, chosen and reused by a context. A Command is a specific request to do something, captured as an object to be executed, queued, logged, or undone.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Smart home remote with undo | Easy | Invoker and receivers. |
| Drawing app with undo/redo and macros | Medium | History management. |