The idea
What it is
Command turns a request into a stand-alone object — so instead of calling an action, you create an object that represents the action, and execute it whenever (and wherever) you like. In one sentence: wrap a request as an object so you can parameterize callers with it, queue it, log it, and undo it. Think of a smart-home remote: pressing a button doesn't flip the light directly. The press creates a LightOnCommand object that bundles what to do (turn on) with what to do it to (the living-room light). The remote just says execute() — it has no idea whether that means a light, a thermostat, or the garage door.
The classic analogy is a restaurant order slip. You tell the waiter what you want, and the waiter doesn't cook — they write your request on a slip and hand it to the kitchen. That slip is a command object: it captures the request, it can sit in a queue on the kitchen rail, the chef picks it up when ready, it can be cancelled, and at the end of the night the stack of slips is a log of everything that happened. The moment a request becomes a physical thing rather than a fleeting function call, all of that becomes possible.
The one sentence to remember
Command makes an action a first-class object with an execute() (and usually an undo()) method. Anything you can do with an object — store it, pass it around, put it on a stack, replay it — you can now do with an action.
The superpower is the history stack
Once every action is an object that knows how to reverse itself, undo is almost free: keep executed commands on a stack, and undo is just history.pop().undo(). This is exactly how editors, drawing apps, and IDEs implement Ctrl+Z.
Mechanics
How it works
The four players
The pattern splits 'a button press turns on a light' into four small roles. The split looks like ceremony at first, but each role is what unlocks one of the pattern's powers:
- Command interface — the tiny contract every action signs:
execute()and (for undoable systems)undo(). This is all the invoker ever sees. - Concrete commands — one small class per action:
LightOnCommand,LightOffCommand,ThermostatUpCommand. Each one holds a reference to its receiver and knows how to perform and reverse exactly one request. - Receiver — the object that does the real work: the
Light, theThermostat. It has ordinary methods (turnOn(),up()) and knows nothing about commands at all. - Invoker — the remote button (or menu item, or job queue worker). It holds some command and calls
execute()on it. It never knows the concrete class — swap which command a button holds and the button's behaviour changes without touching the button.
The client (your setup code) wires it all together: it creates the receivers, creates concrete commands around them, and hands those commands to the invoker. Notice how this echoes [[dependency-injection-and-ioc]] — the button is given its behaviour from outside rather than hard-coding it.
How undo works
Each command knows how to reverse itself: LightOnCommand.undo() turns the light back off; ThermostatUpCommand.undo() steps the temperature back down. The invoker keeps a history stack — every executed command gets pushed on top. Undo pops the most recent command and calls its undo(), which unwinds actions in exact reverse order. Redo, if you want it, is a second stack of undone commands. Real systems cap the stack (say, the last 100 actions), so the oldest commands eventually fall off and can no longer be undone — the prototype's 6-slot stack shows this honestly.
interface Command {
execute(): void;
undo(): void;
}
class LightOnCommand implements Command {
constructor(private light: Light) {} // wraps action + receiver
execute() { this.light.turnOn(); }
undo() { this.light.turnOff(); } // knows its own reverse
}
class RemoteButton { // invoker
private history: Command[] = [];
press(cmd: Command) {
cmd.execute(); // run the request...
this.history.push(cmd); // ...and remember it
}
undo() {
this.history.pop()?.undo(); // reverse the latest one
}
}Look at what RemoteButton doesn't know: no if (button === 'light') branching, no reference to Light or Thermostat, no knowledge of what any command does. It just tells the command to execute — a clean example of [[tell-dont-ask]] at the object level. All the device-specific knowledge lives inside the concrete commands.
You've already met this pattern
- Editor undo/redo — every keystroke, deletion, and format change is a command on a stack; Ctrl+Z pops and reverses it.
- Job and task queues — a background job is a serialized command: created now, queued, executed later by a worker that only knows the
run()interface. - Transactional operations — each step of a multi-step operation is a command with a compensating
undo(), so a failure can roll everything back in reverse. - GUI buttons and menu items — 'Copy' in the toolbar, the Edit menu, and Ctrl+C all hold the same command object; one action, three invokers.
- Macro recording — record the commands a user performs into a list, then replay the list to repeat the whole sequence.
Interactive prototype
See it. Build it. Break it.
A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.
About this simulation
A smart-home remote wired up with the Command pattern. Press 💡 Light on, 💡 Light off, or 🌡 Heat + on the remote (left) and watch the two-hop: a command card (LightOnCommand, ThermostatUpCommand…) pops onto the history stack in the middle first, and only then does the device on the right react — the bulb lights up, the temperature ticks. That middle hop is the pattern: the button never touches the device; it builds an object that does. Now press ⎌ Undo — the top card pops off the stack and the device reverses (light back off, temp back down), unwinding in exact reverse order. The stack holds at most 6 commands (oldest drops off — a bounded undo depth, just like real editors). The history: N chip counts what's undoable.
Hands-on
Try these yourself
Open the prototype above, predict what happens, then verify.
Watch the two-hop
Press 💡 Light on once and watch closely: a LightOnCommand card pops onto the history stack in the middle first, and only then does the bulb on the right light up. The button never touched the light — it built an object, and the object did the work. Press 🌡 Heat + and watch a ThermostatUpCommand do the same for the thermostat.
Fill the stack
Mix presses — 💡 Light on, 🌡 Heat + a few times, 💡 Light off — and watch the history: N chip climb as cards pile on top. Keep going past six presses: the stack is full, so the oldest card drops off the bottom. That command can no longer be undone — you've just discovered why real editors have a bounded undo depth.
Unwind with Undo
Now press ⎌ Undo repeatedly. Each press pops the top card and reverses exactly that action — light back off, temperature back down — in perfect reverse order, until the stack is empty. Press ⎌ Undo once more on the empty stack: nothing breaks, there is simply nothing left to reverse. Hit ↺ Reset and replay any sequence you like.
In practice
When to use it — and what trips people up
Reach for Command when the request itself needs a life of its own
The tell-tale sign is that you need to do something with an action besides just running it right now:
- Undo / redo — the flagship use. Keep executed commands on a stack; each knows how to reverse itself, so Ctrl+Z is a pop and an
undo(). - Queues and scheduling — create a request now, run it later (or elsewhere). Background jobs, thread pools, and task schedulers all pass command-shaped objects to workers that only know
run(). - Decoupling the trigger from the action — toolbar button, menu item, and keyboard shortcut all hold the same command object; UI elements become interchangeable holders of behaviour instead of hard-coding it.
- Macros, logging, and replay — a list of commands is a recording. Replay it for macros, write it to disk as an audit log, or re-run it to recover state after a crash.
When NOT to use it
If a button just needs to call a method — no history, no queue, no undo, no swapping behaviour at runtime — then a plain direct call is better. Wrapping every method call in a command class is pure ceremony: you pay the class-per-action tax and get none of the powers. Add the pattern when a concrete need (undo, queueing, logging) shows up, not before.
What it gives you
- Undo/redo nearly for free — each command reverses itself, so a history stack gives you Ctrl+Z with a pop and an undo() call.
- Decouples invoker from receiver — the button knows only execute(), so you can rewire what a button does without touching the button, and reuse one command from many triggers.
- Actions become data — commands can be queued, scheduled, serialized, logged, and replayed, which is impossible with a bare method call.
- Easy to extend and compose — add a new action by adding one small class (open/closed), or build a macro command that runs a whole list of commands as one.
Common mistakes
- Class proliferation — every distinct action becomes its own class, so a big app can accumulate dozens of tiny command classes (lambdas/closures soften this in modern languages).
- Indirection tax — a simple 'call this method' now takes an interface, a concrete class, and an invoker; readers must hop through layers to find what actually happens.
- Undo is only as correct as each command's undo() — reversing stateful or side-effecting actions (sent emails, API calls) can be hard or impossible, and one buggy undo corrupts the whole history.
- History has real costs — storing commands (and any state snapshots they need for undo) consumes memory, which is why undo stacks are capped and old commands become unrecoverable.
Reference
Code & further reading
A minimal reference implementation and pointers worth bookmarking.
// Receivers — the devices that do the real work.
class Light {
on = false;
turnOn() { this.on = true; }
turnOff() { this.on = false; }
}
class Thermostat {
temp = 20;
up() { this.temp++; }
down() { this.temp--; }
}
// Command — every action signs this tiny contract.
interface Command {
execute(): void;
undo(): void;
}
// Concrete commands — wrap ONE action + its receiver.
class LightOnCommand implements Command {
constructor(private light: Light) {}
execute() { this.light.turnOn(); }
undo() { this.light.turnOff(); } // its own reverse
}
class ThermostatUpCommand implements Command {
constructor(private t: Thermostat) {}
execute() { this.t.up(); }
undo() { this.t.down(); }
}
// Invoker — knows only the interface, keeps history for undo.
class Remote {
private history: Command[] = [];
press(cmd: Command) { cmd.execute(); this.history.push(cmd); }
undo() { this.history.pop()?.undo(); } // pop + reverse
}
// Client wires it together.
const light = new Light();
const remote = new Remote();
remote.press(new LightOnCommand(light)); // light.on === true
remote.undo(); // light.on === falseReferences & further reading
5 sources- Articlerefactoring.guru
Command — Refactoring.Guru
The best illustrated walkthrough: the problem (a toolbar with hard-coded buttons), the four participants, the restaurant-order analogy, and code in many languages. Read this first.
- Bookgameprogrammingpatterns.com
Command — Game Programming Patterns (Bob Nystrom)
A famously readable chapter (free online) that builds Command from a game's input handler up to full undo/redo — the clearest explanation of why 'a request as an object' matters in practice.
- Booken.wikipedia.org
Design Patterns: Elements of Reusable Object-Oriented Software — Gamma, Helm, Johnson, Vlissides
The original 'Gang of Four' book that catalogued Command among the behavioral patterns. The primary source for the formal intent, structure, and participants.
- Articlebaeldung.com
The Command Pattern in Java — Baeldung
A compact Java implementation of the pattern, including the modern take: using lambdas and method references as lightweight commands instead of one class per action.
- Docsen.wikipedia.org
Command pattern — Wikipedia
Concise reference covering terminology (command, receiver, invoker, client), the undo mechanism, and a long list of real uses from GUI buttons to transactional behaviour and wizards.
Knowledge check
Did it land?
Quick questions, answers revealed on submit. Sign in to save your best score.
question 01 / 05
What is the core intent of the Command pattern?
question 02 / 05
In the smart-home example, which object actually knows HOW to turn the light on?
question 03 / 05
How does undo typically work in the Command pattern?
question 04 / 05
The remote button holds a Command but never knows its concrete class. What does that buy you?
question 05 / 05
When is Command the WRONG choice?
0/5 answered