The Command pattern represents a request as an object, allowing an application to parameterize, queue, log, repeat, or undo an action without coupling the trigger to the receiver. By turning a request into a firstâclass object, the pattern lets an application maintain history, schedule work, and keep the caller independent of the implementation.
The Core Idea
interface Command {
execute(): void;
undo(): void;
}
const command = new BoldCommand(editor);
manager.execute(command);
manager.undo();
The manager only needs to know the command's interface; it doesn't need to understand text editing or file deletion specifics. In other words, the manager can invoke any command that conforms to the interface without caring whether the command bolds text, deletes a file, or performs any other operation.
The Participants
Command: defines operations such as
executeand, when appropriate,undo.
It establishes the contract that all concrete commands must follow.Concrete Command: holds the receiver and request data.
It encapsulates the details needed to carry out the action.Invoker: executes commands and can maintain a history.
It acts as the clientâfacing component that triggers the command.Receiver: performs the actual work.
The receiver knows how to carry out the operation that the command requests.
class CommandManager {
private history: Command[] = [];
execute(command: Command) {
command.execute();
this.history.push(command);
}
undo() {
this.history.pop()?.undo();
}
}
For an undo operation to be effective, it must capture enough state to reverse the operation correctly. Simply calling the same method twice is not always a valid way to undo an action. Therefore each concrete command must store the information required to restore the previous state before it modifies anything.
Applications
Commands are particularly useful for UI actions with multiple triggers, macro operations, background queues, and audit logs:
They let disparate parts of the UI invoke the same behavior without duplicating code, and they provide a natural way to record what happened for later replay or analysis.
class MacroCommand implements Command {
constructor(private commands: Command[]) {}
execute() {
for (const command of this.commands) command.execute();
}
undo() {
for (const command of [...this.commands].reverse()) command.undo();
}
}
They can also support rollback workflows. However, database transactions and distributed compensation require stronger guarantees than an inâmemory command list can provide. In practice, a simple command list can undo changes only while the program remains alive; persistent or distributed systems need transactional support beyond what the pattern alone offers.
When to Use It
Use the Command pattern when actions need to be queued, serialized, audited, composed, or undone. Avoid it for simple oneâoff calls where object creation and history management would only add unnecessary complexity.
For a single UI callback, closures are often sufficient. Command objects become more valuable when the same action is invoked from multiple places or has a lifecycle of its own. In those cases the command can be stored, passed around, or scheduled for later execution.
Related Patterns
Memento captures state snapshots, while Command stores actions. Composite can represent a macro of commands. Strategy encapsulates an algorithm, whereas Command encapsulates a request. Thus each pattern solves a distinct problem, but they can be combined, for example, a Composite command may use Memento objects to restore state.
The Bottom Line
Command makes actions firstâclass objects, providing a practical solution when an application needs to maintain history, manage queues, handle macros, or achieve a clean separation between an action's trigger and its implementation.

Comments (0)
Please sign in to leave a comment