If you remember five things
The whole pattern in one glance. Everything below defends these five calls.
- An action becomes a value. A command object carries the operation, the receiver, and the arguments, so you can store it, queue it, log it, and reverse it. A plain method call can do none of that.
- The invoker stays blind. It only knows the
Commandinterface, never the receiver. That missing dependency edge is the entire payoff. - Reach for it on undo, queue, or replay. If the prompt says undo, redo, schedule, or audit log, this is the pattern the examiner wants.
- Capture undo state at execute time. Snapshot the old value inside
execute()right before you mutate, never in the constructor, or undo restores a stale value. - It is not Strategy. Strategy swaps an algorithm to compute a result. Command captures a request with a receiver and side effects so it can be stored and undone.
1The problem it solves#
Start with the coupling pain, because that is what the pattern actually cures. Picture a text editor with three ways to bold the selection: a toolbar button, a keyboard shortcut, and a right-click menu.
- The naive version has all three call
editor.bold()directly. - Now add undo. Then a macro recorder. Then "repeat last action."
- Every trigger now knows the editor's internals, and undo becomes a giant switch statement trying to reverse things it never recorded.
The real pain is coupling.
- The thing that triggers an action (a button) is welded to the thing that performs it (the editor), and to the exact way it performs it.
- That violates the Single Responsibility Principle: the button now owns both "I was clicked" and "here is how bold works."
- You cannot store the action for later, replay it, or reverse it, because nobody wrote down what "it" was.
What I actually want is to treat an action as a value.
- Something I can put in a list, hand to a worker thread, serialize to a log file, and ask to reverse itself.
- A method call happens and then it is gone, along with the arguments and the context it ran in.
- A command object keeps all of that around.
2The idea (mental model)#
Take the request and make it an object that holds everything needed to perform the action later: which operation, on which receiver, with what arguments. It exposes one method, usually execute(). If you need undo, it also remembers enough state to offer undo().
The picture I use at the whiteboard: a restaurant order ticket.
- The waiter (the caller) does not cook. They write the order on a ticket and stick it on the rail.
- The ticket is the command object. The kitchen (the receiver) picks it up and does the work.
- The waiter has no idea how the risotto is made. The kitchen has no idea which waiter wrote the ticket.
- Because the ticket is a physical thing, you can stack tickets, reorder them, and keep them as a record.
Four roles fall out of this:
- Command: the interface with
execute()(and maybeundo()). - Concrete Command: a specific ticket that knows its receiver and arguments.
- Receiver: the object that actually does the work.
- Invoker: the thing that holds commands and triggers them. It only knows the
Commandinterface.
The decoupling between Invoker and Receiver is the whole payoff. It is the Dependency Inversion Principle in action: the invoker depends on an abstraction, never on the concrete receiver. If you can state that out loud, you have shown you understand why the pattern exists, not just how to draw it.
3Structure#
Wire it once, then fire blind. The client creates the receiver, builds the concrete commands, and loads them into the invoker. After that wiring step, the invoker fires commands knowing nothing about what they do.
- The invoker depends only on the
Commandinterface. - Each concrete command holds a reference to its receiver and carries any parameters it needs.
- That wiring step is the point: it is the only place that knows both sides.
RemoteControl has no arrow to Light. The remote only touches the Command interface, while the two concrete commands each hold a Light. That missing edge from invoker to receiver is the pattern working.4Implementation in Java#
Here is a remote control with undo. I kept the receiver trivial so the pattern stays in focus.
interface Command {
void execute();
void undo();
}
// Receiver: the thing that does real work
class Light {
private final String room;
private boolean on = false;
Light(String room) { this.room = room; }
void on() { on = true; System.out.println(room + " light ON"); }
void off() { on = false; System.out.println(room + " light OFF"); }
boolean isOn() { return on; }
}
class LightOnCommand implements Command {
private final Light light;
LightOnCommand(Light light) { this.light = light; }
public void execute() { light.on(); }
public void undo() { light.off(); }
}
class LightOffCommand implements Command {
private final Light light;
LightOffCommand(Light light) { this.light = light; }
public void execute() { light.off(); }
public void undo() { light.on(); }
}
The invoker holds a history stack so undo walks backward through what actually ran:
import java.util.ArrayDeque;
import java.util.Deque;
class RemoteControl {
private final Deque<Command> history = new ArrayDeque<>();
void press(Command cmd) {
cmd.execute();
history.push(cmd);
}
void undoLast() {
if (history.isEmpty()) return;
history.pop().undo();
}
}
Wiring and use:
public class Demo {
public static void main(String[] args) {
Light kitchen = new Light("Kitchen");
RemoteControl remote = new RemoteControl();
remote.press(new LightOnCommand(kitchen)); // Kitchen light ON
remote.press(new LightOffCommand(kitchen)); // Kitchen light OFF
remote.undoLast(); // Kitchen light ON
remote.undoLast(); // Kitchen light OFF
}
}
Two things worth calling out. First, undo is easy here and harder in real life.
- Turning a light off is the exact inverse of turning it on, so
undo()is trivial in this toy. - A command that sets a value should store the old value before it overwrites, so it can restore it.
- Capture that snapshot inside
execute(), not in the constructor, because the receiver's state at construction time may differ from its state when the command finally runs. - For full-state undo instead of inverse operations, the Memento pattern pairs in: the command grabs a memento before mutating and restores it on undo.
execute(). Three commands run between construction and this one running. What does undo restore?Reveal the answer
execute(), the moment before you mutate. Getting this wrong is the single most common undo bug in the room.Second, a composite trick interviewers love. You can make a command that runs a list of commands, which gives you macros for free. This is Command married to Composite, a single command that is also a tree of commands:
import java.util.List;
import java.util.Collections;
class MacroCommand implements Command {
private final List<Command> commands;
MacroCommand(List<Command> commands) { this.commands = commands; }
public void execute() {
for (Command c : commands) c.execute();
}
public void undo() {
// reverse order matters: undo last thing first
List<Command> reversed = new java.util.ArrayList<>(commands);
Collections.reverse(reversed);
for (Command c : reversed) c.undo();
}
}
5A real-world example#
The clearest place I have shipped this is a job queue. Users kick off exports, report generations, and bulk emails, and none of that should run on the web request thread.
- A report that takes 8 seconds would hold an HTTP connection open and block the thread pool.
- So the handler builds a command object,
new GenerateReportCommand(reportId, userId), and pushes it onto a queue. - A pool of worker threads pulls commands off and calls
execute(). - The web tier knows nothing about report generation. The workers know nothing about HTTP. The queue in the middle is just a list of commands.
This is exactly how task frameworks model work.
- A
Runnablein Java is the Command pattern stripped down to one method,run(). - Hand a
Runnableto anExecutorServiceand the executor is your invoker, your lambda is the concrete command. - People use this every day without naming it.
Callableis the same thing when you want a return value back.
It also shows up anywhere there is transaction logging.
- If every state change is a command, you can write each one to a log before running it.
- Crash and restart? Replay the log and you are back where you were. That is write-ahead logging, and databases and message brokers lean on it hard.
- Editors use the same trick: the undo stack is a list of executed commands, and redo is a second stack of commands you popped off.
In LLD interviews you will meet this pattern wearing a costume: a text editor, a vending machine, a smart home remote, a drawing app, a bank or wallet transaction system. If the prompt says undo, redo, replay, schedule, or queue, I am handing you the Command pattern on purpose and watching to see if you catch it.
6When to use it, and when not to#
Reach for Command when any of these are true:
- You need undo or redo.
- You want to queue, schedule, or delay operations.
- You want to log operations so you can replay or audit them.
- The same action is triggered from several places (menu, shortcut, button) and you are tired of duplicating the call.
- You want to compose small operations into macros.
Skip it when the action is a plain, immediate method call with no undo, no queueing, and one caller.
- Wrapping
light.on()in a class earns you nothing there. It just adds a file. - I have seen candidates bury a four-line feature under five command classes to show off, and it reads as cargo-culting, not design sense.
- The pattern is a tool for decoupling and deferral. If you are neither decoupling callers nor deferring execution, you are paying the ceremony tax for no reason.
If you cannot answer "why can't this just be a method call," you do not need the pattern yet.
7Common mistakes#
Putting business logic in the command.
- The concrete command should be thin: it holds a receiver and delegates.
- When I see a
TransferMoneyCommanddoing balance math, locking, and validation insideexecute(), that logic belongs on the receiver (the account service). - The command's job is to say "call this, with these arguments," not to be the implementation. Keep it skinny and the receiver stays testable on its own.
Forgetting to capture undo state at the right time.
- A classic bug: the command stores the receiver's old value in its constructor, but it runs much later after other commands changed that value.
- Now undo restores a stale value.
- Capture the state to reverse inside
execute(), right before you mutate.
Undoing macros in the wrong order.
- If a macro ran A then B then C, undo must run C then B then A.
- Reverse the list. Candidates forget this constantly and it is an easy point to lose.
Making the invoker too smart.
- The whole value is that the invoker only knows the
Commandinterface. - The second it starts type-checking commands with
instanceofand branching, you have rebuilt the switch statement you were trying to kill, and broken the Open/Closed Principle you were trying to honor.
Calling it Command when you built a Strategy, or the reverse. Both wrap behavior in an object, so the shallow answer blurs them. Strategy swaps one interchangeable algorithm for another to compute a result. Command captures a request, usually with a receiver and side effects, so it can be stored, queued, and undone. If you cannot cleanly say which one you built and why, I read it as pattern-name bingo, not design judgment.
8Where it shows up in interviews#
Expect to meet it under a different name. The prompt rarely says "use the Command pattern."
- It says "design a text editor with undo and redo," or "design a remote control for a smart home," or "build a task scheduler."
- Your move is to name the four roles out loud: here is my
Commandinterface, here are the concrete commands, this class is the receiver, this one is the invoker holding the history stack. - Naming the roles early signals you have seen this before.
If you want a sequence to picture, here is the runtime flow for a press-then-undo:
Light. Remote calls execute() and undo() on the command, and the command is the only thing that reaches the receiver. The history push between the two client calls is what makes undo possible: the remote kept the command so it could ask it to reverse itself later."How does redo work?"
execute() again. Clear the redo stack whenever a fresh command runs, because a new action invalidates the old redo history."How do you queue these across threads?"
BlockingQueue works) feeding a worker pool. Commands must be self-contained, so pass data by value, not references that can mutate underneath you between enqueue and execute."How would you persist and replay?"
"What is the difference from Strategy?"
"When would you not use it?"
What you can now explain in the room
- Why turning an action into an object is what lets you queue, log, and undo it.
- The four roles and why the invoker depending only on the interface is the entire payoff.
- Why you capture undo state inside
execute()and not in the constructor. - Why macro undo runs the sub-commands in reverse order.
- How a job queue, a
Runnable, and write-ahead logging are all this pattern in disguise. - The clean one-liner that separates Command from Strategy under pressure.
Next, look at the Memento pattern (another way to build undo by snapshotting state) and the classic "design a text editor with undo/redo" LLD problem where Command and Memento often team up.
Get a prep plan built around your Command Pattern interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.