Skip to content

Command

Command turns a request into a first-class object that carries everything needed to perform an action — and, crucially, to reverse it. Because the request is an object, it can be queued, logged, passed to whoever will run it later, and pushed onto a history stack to support undo.

A text editor needs typing, deleting, and formatting, all triggered from menus, buttons, and keyboard shortcuts. If each control calls editor methods directly, the controls and the editor are welded together, and a feature like undo becomes a nightmare: there is no record of what happened, only the resulting state. To undo, you would have to reconstruct the inverse of every possible action inline.

The missing concept is the request itself as a value you can keep. Command makes each action an object with an execute method and an undo method. A control just hands a command to an invoker; the invoker runs it and remembers it on a stack. Undo is then uniform: pop the last command and call its undo. The editor no longer cares who triggered the action.

classDiagram
  class Command {
    <<interface>>
    +execute()
    +undo()
  }
  class InsertText {
    +execute()
    +undo()
  }
  class Editor {
    -text: string
    +insert(s)
    +deleteLast(n)
  }
  class History {
    -stack: List~Command~
    +run(cmd)
    +undo()
  }
  Command <|.. InsertText
  InsertText --> Editor : acts on
  History o--> Command : remembers
A History invoker runs Command objects that act on the Editor receiver and can be undone
  • Command — the interface declaring execute and undo.
  • Concrete Command — binds a receiver to an action and stores whatever it needs to reverse that action.
  • Receiver — the object that does the real work; here, the editor that holds the text.
  • Invoker — triggers commands and keeps the history; here, the history stack that also drives undo.
  • Client — creates concrete commands and hands them to the invoker.

An editor with insert commands recorded on a history stack so the last action can be undone. Each command stores enough information to reverse itself.

class Editor {
text = '';
insert(s: string): void {
this.text += s;
}
deleteLast(n: number): void {
this.text = this.text.slice(0, -n);
}
}
interface Command {
execute(): void;
undo(): void;
}
class InsertText implements Command {
constructor(private editor: Editor, private value: string) {}
execute(): void {
this.editor.insert(this.value);
}
undo(): void {
this.editor.deleteLast(this.value.length);
}
}
class History {
private stack: Command[] = [];
run(cmd: Command): void {
cmd.execute();
this.stack.push(cmd);
}
undo(): void {
const cmd = this.stack.pop();
if (cmd) cmd.undo();
}
}
const editor = new Editor();
const history = new History();
history.run(new InsertText(editor, 'hello '));
history.run(new InsertText(editor, 'world'));
console.log(editor.text); // "hello world"
history.undo();
console.log(editor.text); // "hello "
  • Pro: decouples the object that triggers an action from the object that performs it.
  • Pro: because each request is an object, you get undo, redo, queues, logging, and macros almost for free.
  • Pro: new commands are added without changing the invoker or the receiver.
  • Con: every action becomes a class, which is a lot of ceremony for a one-off call.
  • Con: reliable undo requires each command to capture enough state to reverse itself, which can be tricky for complex operations.
  • Memento is a common partner: instead of computing an inverse, a command can snapshot state with a memento and restore it on undo.
  • Strategy also wraps behaviour in an object, but a strategy is chosen to vary an algorithm, while a command represents a specific deferred request.
What does the Command pattern encapsulate?
What role does the invoker play?
Why is undo natural with Command?
Which is a genuine downside of Command?