Design Patterns · Behavioral Patterns
Command
Turn a request into an object with its receiver and arguments. Reuse invocation logic, defer work, or record reversible operations.
This lesson follows Chain of Responsibility. A command packages an operation rather than deciding which handler receives it.
Command represents a request as an object. It stores the information needed to execute that request through a common method. This lets callers pass, queue, or record operations. The cost is another representation and rules for execution state.
Keep input controls separate from the operation
An editor's toolbar button and keyboard shortcut both append text. Copying edit logic into both controls ties the interface to the editor implementation. Connect both controls to a command contract instead.
interface Command { execute(): void }
interface UndoableCommand extends Command { undo(): void }
class Editor { text = "draft"; }
class AppendText implements UndoableCommand {
private before: string | undefined;
private executed = false;
constructor(private editor: Editor, private addition: string) {}
execute(): void {
if (this.executed) throw new Error("Use a fresh command per edit");
this.before = this.editor.text;
this.editor.text += this.addition;
this.executed = true;
}
undo(): void {
if (!this.executed || this.before === undefined) {
throw new Error("No edit to undo");
}
this.editor.text = this.before;
this.before = undefined;
}
}
class Button {
constructor(private command: Command) {}
click() { this.command.execute(); }
}
const editor = new Editor();
const edit = new AppendText(editor, " revised");
new Button(edit).click();
editor.text; // draft revised
edit.undo(); // draft
This is a single-use edit command with one undo. A UI that creates repeated edits must construct a fresh command per action. A stateless save command can be reusable. Do not confuse sharing an operation definition with sharing one mutable undo record.
Record enough information to reverse work
Try it
Inspect an edit and its undo record
editor.text = draft
command.addition = ' revised'
no previous state captured yet
before = draft
editor.text = draft revised
history records the successful command
restore before
editor.text = draft
undo record consumed
Step through
Undo in a history stack
Execute and record
execute A → record A
execute B → record B
Record after successful execution. Failed or no-op operations need explicit history rules.
Undo the latest operation first
pop B → undo B
pop A → undo A
A snapshot-based undo assumes no unrelated change slipped in. Restoring an old whole-document snapshot can overwrite concurrent edits.
Choose a redo rule
redo requires retained data and a defined re-execution policy
The sample consumes its undo record and disallows re-execution. A full editor needs redo, history limits, and a policy for new edits after undo.
A request object does not guarantee reversible effects
An email cannot be unsent by restoring a local string. External operations may require compensation instead of exact undo. A queued command also needs serialization, receiver lookup, and retry semantics. Object references alone do not survive process restart.
Map the roles
| Role | In this example | Job |
|---|---|---|
| Sender (invoker) | Button, the keyboard shortcut | Starts requests. Holds a reference to a command and triggers it instead of calling the receiver. Usually receives a pre-built command from the client. |
| Command | Command, UndoableCommand | Declares a single method for executing the command, plus undo() when reversal is supported |
| Concrete commands | AppendText | Implement a kind of request. They do not do the real work themselves. They pass the call to a receiver, with the arguments stored in their fields. |
| Receiver | Editor | Contains the business logic. Almost any object can be a receiver. |
| Client | the code that wires a button to new AppendText(editor, " revised") | Creates and configures concrete commands, passing every request argument and the receiver into the command's constructor |
Check yourself
Who does the actual text editing when the button is clicked?
Not quite. The button only triggers the command. It knows nothing about editing.
Correct. Concrete commands usually delegate. The receiver holds the business logic.
Reach for it when a request needs a life of its own
- you want to parameterize objects with operations. A command turns a method call into an object. You can pass it as an argument, store it in another object, or swap it at runtime. A configurable context menu, where each item triggers a command chosen by the user, is a classic case.
- you want to queue operations, schedule them, or run them remotely. A command can be serialized into a string, written to a file or a database, and restored later. The same trick lets you queue, log, or send commands over the network.
- you want reversible operations. Command is the most popular way to build undo and redo. You keep a history stack of executed commands with backups of the state they changed.
Undo has two catches. Saving application state is hard when some of it is private, which Memento helps with. State backups can also use a lot of memory, so some commands perform the inverse operation instead, which can be hard or impossible.
Check yourself
A settings screen lets users bind any toolbar slot to any action, such as save, print, or share. Which Command use is this?
Not quite. Nothing is deferred. The action runs when the slot is clicked.
Correct. Each slot stores a command object chosen at runtime.
Implement it in five steps
Refactor existing code toward the pattern in this order:
- Declare the command interface with a single execution method.
- Extract requests into concrete command classes that implement the interface. Each class gets fields for the request arguments and a reference to the receiver, all set through the constructor.
- Identify the classes that act as senders. Add fields that store commands. Senders talk to commands only through the command interface, and they usually get commands from client code instead of creating them.
- Change the senders so they execute the command instead of sending a request to the receiver directly.
- Have the client initialize objects in this order: create the receivers, create the commands and link them to receivers, then create the senders and link them to commands.
Check yourself
Why does the client create the receiver before the command?
Correct. Receivers come first, then commands, then the senders that hold them.
Not quite. Receivers know nothing about commands in this pattern.
Name what it costs
| You gain | You pay |
|---|---|
| Classes that invoke operations are decoupled from classes that perform them (single responsibility) | A whole new layer between senders and receivers makes the code more complicated |
| New commands without breaking existing client code (open/closed) | Undo history consumes memory, and inverse operations can fail |
| Undo and redo | A durable queue needs serializable data and execution rules |
| Deferred execution of operations | Command objects or request records to maintain |
| Several simple commands combined into one complex command, a macro |
Check yourself
A "Format document" button should run trim, reindent, and sort-imports as one undoable step. Which benefit of Command applies?
Correct. A macro is itself a command, so it can be executed and undone as a unit.
Not quite. Nothing here is delayed. The point is bundling.
Do not confuse it with its neighbors
| Pattern | How it relates |
|---|---|
| Chain of Responsibility, Mediator, Observer | All four connect senders and receivers differently. Command creates a one-way connection between a sender and a receiver. Chain passes a request along possible receivers, Mediator routes through a central object, and Observer lets receivers subscribe dynamically. |
| Chain of Responsibility | Chain handlers can be implemented as commands. Or the request passed down a chain can itself be a command. |
| Memento | Use them together for undo. Commands perform operations on a target object. Mementos save that object's state just before a command runs. |
| Strategy | Both parameterize an object with some action, but with different intent. Command turns any operation into an object you can defer, queue, store, or send. Strategy describes different ways to do the same thing and lets you swap algorithms in one context. |
| Prototype | Helps when you need to save copies of commands into a history. |
| Visitor | Think of Visitor as a more powerful Command, whose objects can run operations over objects of different classes. |
Check yourself
Fastest and cheapest routes are interchangeable ways to calculate one route. Which intent is closer?
Not quite. Command is useful when a specific request needs its own execution lifecycle.
Correct. The variation is the algorithm used for the same job.
Retrieve and apply
Check yourself
A queued command stores a receiver object reference in memory. Can it automatically run after a server restart?
Correct. The object reference belonged to the old process. Durable execution needs a stored representation and a new execution context.
Not quite. Being an object does not provide persistence, deserialization, or receiver reconstruction.
Define a rename command's arguments, receiver, and undo data. Continue to Iterator to separate traversal state from collection storage.
Source: Alexander Shvets, Dive Into Design Patterns (深入设计模式), Chinese edition v2021-1.25. Command, printed pages 253–271. Explanations, examples, and exercises are adapted for this course.