Command Design Pattern
Problem
Some objects need to ask for work to happen, but they should not know the exact object that performs the work, which method will be called, or whether the request will be executed now, queued for later, logged, repeated, or undone (Gamma et al. 1995).
This often starts innocently:
if (actionName.equals("light:on")) {
light.on();
} else if (actionName.equals("light:off")) {
light.off();
} else if (actionName.equals("stereo:on")) {
stereo.on();
stereo.setInput("CD");
}
The code works, but the caller has become a dispatcher, a receiver selector, and an action implementation all at once. As the request list grows, the dispatcher becomes harder to extend, test, queue, log, and undo.
Context
Use Command when requests must become first-class objects. The pattern is a strong fit when a system needs to parameterize objects with requests, queue or log requests, support undo, or replace a dispatcher whose request-handling branches are becoming too rigid (Gamma et al. 1995; Kerievsky 2004).
- Buttons, menu items, keyboard shortcuts, or remote-control slots should be configured with actions at runtime.
- Requests need to be queued, scheduled, retried, logged, or sent to another process.
- The system needs undo and redo, and each operation knows how to reverse itself or restore prior state.
- Several smaller operations should be bundled into a macro command.
- A conditional dispatcher is growing because every new action adds another branch.
Do not apply it automatically. If a method contains two stable branches and no need for undo, logging, queuing, or runtime configuration, a direct method call or small conditional is easier to read.
Research Synthesis
The Gang of Four version supplies the core role model: a Command object encapsulates a request, an Invoker stores and triggers commands, and a Receiver does the real work. The important consequence is decoupling: the object that asks for work no longer needs to know which receiver method performs it (Gamma et al. 1995).
Head First Design Patterns makes the pattern concrete with a home-automation remote control. The remote knows only “press slot 0”; command objects know whether that means light.on(), light.off(), a ceiling fan speed change, a no-op placeholder, an undo operation, or a “party mode” macro (Freeman and Robson 2020).
Refactoring to Patterns gives the best adoption rule: refactor toward Command when conditional dispatch has either outgrown its class or needs runtime flexibility. The practical path is to extract each branch into an execution method, extract those methods into command classes, give them a common signature, then replace the dispatcher with a command map (Kerievsky 2004).
Solution
Create a small object for each request. The invoker stores commands and calls the same method on all of them, usually execute(). A concrete command binds a receiver to one operation, plus any arguments or previous state needed to perform the request safely (Gamma et al. 1995).
UML Role Diagram
The diagram should show one idea: the invoker depends only on the Command interface; concrete commands decide which receiver work is done.
Example: Remote Control
The remote-control example is useful because it demonstrates the pattern’s full range without inventing infrastructure. A slot can hold a light command today, a stereo command tomorrow, or a macro command later. The remote does not change (Freeman and Robson 2020).
UML Example Diagram
Sequence Diagram
The sequence diagram captures the runtime point that class diagrams cannot: undo is just another message to the last command object, not special knowledge inside the remote.
Refactoring Path
Kerievsky’s refactoring is especially useful because it prevents pattern-first design. Start with working code, then refactor only when the dispatcher has real pressure on it (Kerievsky 2004).
- Extract the body of each branch into a well-named execution method.
- Extract each execution method into a concrete command class.
- Look across those classes and choose the smallest common execution signature.
- Introduce a
Commandinterface or abstract class. - Put concrete commands in a map keyed by command name, button slot, route name, or message type.
- Replace the conditional dispatcher with lookup plus
execute().
This is not just “remove a switch statement.” It changes the design from “the dispatcher knows every action” to “the dispatcher hosts independently configurable actions.”
Code Example
The same remote-control design appears below in Java, C++, Python, and JavaScript. The class names stay intentionally parallel so you can compare the shape of the pattern rather than the syntax of each language.
interface Command {
void execute();
void undo();
}
final class Light {
void on() {
System.out.println("Light is on");
}
void off() {
System.out.println("Light is off");
}
}
final class NoCommand implements Command {
public void execute() { }
public void undo() { }
}
final class LightOnCommand implements Command {
private final Light light;
LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
final class LightOffCommand implements Command {
private final Light light;
LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.off();
}
public void undo() {
light.on();
}
}
final class RemoteControl {
private Command onCommand = new NoCommand();
private Command offCommand = new NoCommand();
private Command undoCommand = new NoCommand();
void setCommands(Command onCommand, Command offCommand) {
this.onCommand = onCommand;
this.offCommand = offCommand;
}
void pressOn() {
onCommand.execute();
undoCommand = onCommand;
}
void pressOff() {
offCommand.execute();
undoCommand = offCommand;
}
void pressUndo() {
undoCommand.undo();
}
}
public class Demo {
public static void main(String[] args) {
Light light = new Light();
RemoteControl remote = new RemoteControl();
remote.setCommands(
new LightOnCommand(light),
new LightOffCommand(light)
);
remote.pressOn(); // Light is on
remote.pressUndo(); // Light is off
}
}
#include <iostream>
#include <memory>
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
};
class Light {
public:
void on() {
std::cout << "Light is on\n";
}
void off() {
std::cout << "Light is off\n";
}
};
class NoCommand : public Command {
public:
void execute() override { }
void undo() override { }
};
class LightOnCommand : public Command {
public:
explicit LightOnCommand(std::shared_ptr<Light> light)
: light_(std::move(light)) { }
void execute() override {
light_->on();
}
void undo() override {
light_->off();
}
private:
std::shared_ptr<Light> light_;
};
class LightOffCommand : public Command {
public:
explicit LightOffCommand(std::shared_ptr<Light> light)
: light_(std::move(light)) { }
void execute() override {
light_->off();
}
void undo() override {
light_->on();
}
private:
std::shared_ptr<Light> light_;
};
class RemoteControl {
public:
RemoteControl()
: onCommand_(std::make_shared<NoCommand>()),
offCommand_(std::make_shared<NoCommand>()),
undoCommand_(std::make_shared<NoCommand>()) { }
void setCommands(std::shared_ptr<Command> onCommand,
std::shared_ptr<Command> offCommand) {
onCommand_ = std::move(onCommand);
offCommand_ = std::move(offCommand);
}
void pressOn() {
onCommand_->execute();
undoCommand_ = onCommand_;
}
void pressOff() {
offCommand_->execute();
undoCommand_ = offCommand_;
}
void pressUndo() {
undoCommand_->undo();
}
private:
std::shared_ptr<Command> onCommand_;
std::shared_ptr<Command> offCommand_;
std::shared_ptr<Command> undoCommand_;
};
int main() {
auto light = std::make_shared<Light>();
RemoteControl remote;
remote.setCommands(
std::make_shared<LightOnCommand>(light),
std::make_shared<LightOffCommand>(light)
);
remote.pressOn(); // Light is on
remote.pressUndo(); // Light is off
}
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self) -> None:
pass
@abstractmethod
def undo(self) -> None:
pass
class Light:
def on(self) -> None:
print("Light is on")
def off(self) -> None:
print("Light is off")
class NoCommand(Command):
def execute(self) -> None:
pass
def undo(self) -> None:
pass
class LightOnCommand(Command):
def __init__(self, light: Light) -> None:
self.light = light
def execute(self) -> None:
self.light.on()
def undo(self) -> None:
self.light.off()
class LightOffCommand(Command):
def __init__(self, light: Light) -> None:
self.light = light
def execute(self) -> None:
self.light.off()
def undo(self) -> None:
self.light.on()
class RemoteControl:
def __init__(self) -> None:
self.on_command = NoCommand()
self.off_command = NoCommand()
self.undo_command = NoCommand()
def set_commands(self, on_command: Command, off_command: Command) -> None:
self.on_command = on_command
self.off_command = off_command
def press_on(self) -> None:
self.on_command.execute()
self.undo_command = self.on_command
def press_off(self) -> None:
self.off_command.execute()
self.undo_command = self.off_command
def press_undo(self) -> None:
self.undo_command.undo()
light = Light()
remote = RemoteControl()
remote.set_commands(LightOnCommand(light), LightOffCommand(light))
remote.press_on() # Light is on
remote.press_undo() # Light is off
class Command {
execute() {
throw new Error("execute() must be implemented");
}
undo() {
throw new Error("undo() must be implemented");
}
}
class Light {
on() {
console.log("Light is on");
}
off() {
console.log("Light is off");
}
}
class NoCommand extends Command {
execute() {}
undo() {}
}
class LightOnCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() {
this.light.on();
}
undo() {
this.light.off();
}
}
class LightOffCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() {
this.light.off();
}
undo() {
this.light.on();
}
}
class RemoteControl {
constructor() {
this.onCommand = new NoCommand();
this.offCommand = new NoCommand();
this.undoCommand = new NoCommand();
}
setCommands(onCommand, offCommand) {
this.onCommand = onCommand;
this.offCommand = offCommand;
}
pressOn() {
this.onCommand.execute();
this.undoCommand = this.onCommand;
}
pressOff() {
this.offCommand.execute();
this.undoCommand = this.offCommand;
}
pressUndo() {
this.undoCommand.undo();
}
}
const light = new Light();
const remote = new RemoteControl();
remote.setCommands(new LightOnCommand(light), new LightOffCommand(light));
remote.pressOn(); // Light is on
remote.pressUndo(); // Light is off
In languages with first-class functions, a command can sometimes be just a function or closure. That is fine for simple “execute only” callbacks. Use an explicit command object when the request needs identity, metadata, validation, authorization, undo state, serialization, composition, or test seams.
Design Decisions
Execute Only vs. Execute and Undo
The smallest command interface has only execute(). Add undo() only when the product actually needs undo or redo. Undo is not automatic: each command must either store enough old state to restore the receiver or know the inverse operation. Commands that cannot be undone should say so explicitly rather than pretending.
Constructor Arguments vs. Execute Arguments
Some commands receive all data in the constructor:
new PasteCommand(editor, clipboardText)
Others receive a request object at execution time:
command.execute(requestContext)
Constructor arguments make commands self-contained, which helps queuing and logging. Execute arguments keep reusable command objects small, which helps dispatch tables and web handlers. Pick one common signature per command family.
Receiver-Centric vs. Smart Commands
A simple command just forwards one call to a receiver. A smarter command may validate permissions, store previous receiver state, coordinate several receiver calls, or emit domain events. Keep that logic inside the command only when it belongs to the request itself. If commands start becoming mini services with unrelated responsibilities, the pattern is hiding a design problem.
Null Command
A NoCommand object is the Null Object version of Command. It lets an invoker safely call execute() without repeated null checks. This is useful for default remote-control slots, disabled menu actions, optional hooks, or empty macro steps.
Macro Command
A macro command stores a list of commands and implements the same interface. execute() runs each child command in order. undo() usually runs the same child commands in reverse order, because the last executed command is normally the first one that must be reversed.
Queued and Logged Commands
For queues, retries, and transaction logs, the command must carry stable data rather than live object references. A command like “email user 42 with template welcome” can be serialized. A command holding a raw in-memory User object usually cannot. This is the point where Command overlaps with messages, jobs, and event-driven architecture.
Consequences
The main benefit is decoupling. Invokers can be configured with new commands without changing their code, and receivers can evolve without forcing every button, menu item, queue worker, or dispatcher to know their full API.
The costs are real:
- More classes or functions exist in the design.
- The actual receiver method is one indirection away, so tracing execution takes more navigation.
- Undo requires careful state management; a command that only knows “do” does not magically know “undo.”
- Overuse turns straightforward method calls into an abstraction maze.
The pattern earns its complexity when requests need a lifecycle: configure, execute, remember, undo, replay, queue, log, retry, compose, or inspect.
Good Examples
| Example | Why Command fits |
|---|---|
| GUI buttons, toolbar actions, and menu items | The same button/menu framework can invoke any action object. Java Swing’s Action is used by buttons, menus, toolbars, and action maps (Oracle 2026). |
| Undoable editor operations | Each edit can store enough state to undo or redo itself. Java Swing’s UndoableEdit and UndoManager are a direct production example of this idea (Oracle 2026). |
| Job queues | A job object packages work so it can be delayed, retried, distributed, or logged. |
| Game input replay | Player input commands can be recorded, replayed, reversed, or sent over a network. |
| Transaction scripts and workflow steps | A workflow engine can execute a sequence of command objects without embedding each concrete operation in the engine. |
| CLI subcommands | Each subcommand can parse its own options and implement a common run() method. |
Check Yourself
Command Pattern Flashcards
Key roles, refactoring triggers, undo mechanics, and trade-offs of the Command design pattern.
What problem does the Command pattern solve?
What are the core roles in the Command pattern?
When does Refactoring to Patterns recommend moving from a conditional dispatcher to Command?
How does Command support undo?
What is a Null Command?
How is Command different from Strategy?
What does the Receiver do in Command?
What does the Invoker know about a command?
What is a Macro Command?
When is a closure or function enough instead of a command object?
What is the constructor-argument style of Command?
What is the main cost of Command?
Command Pattern Quiz
Test your understanding of Command roles, refactoring triggers, undo, macro commands, null commands, and appropriate use.
A toolbar button should be configurable with Save, Export, Print, or Upload behavior without changing the toolbar class. Which Command role does the toolbar play?
A web controller has a 300-line if/else block dispatching action names to request handlers. Product now wants new actions loaded from configuration. Which refactoring is the best fit?
A LightOnCommand supports undo by calling light.off(). What must a SetThermostatCommand usually store to undo safely?
A “party mode” button turns on lights, starts music, and lowers blinds. The button should still look like one command to the remote. Which variation is this?
When is Command probably over-engineering?
In LightOnCommand, the command stores a Light object and calls light.on() in execute(). Which role does Light play?
Which requirements are good evidence that Command may be worth introducing?
A remote-control slot has not been configured yet, but the remote should still be able to call execute() without checking for null. What should the slot contain?
A job queue serializes work items to disk, restarts, then replays unfinished work. Which Command design decision matters most?
In a small script, a menu option only calls one function immediately and will never need undo, logging, queuing, or runtime reconfiguration. What is the most pragmatic choice?
Put the refactoring path from a conditional dispatcher toward Command in a reasonable order.
Find a dispatcher whose branches represent separate requests.Extract each branch's behavior behind a consistent execution method.Move each request into a concrete command object.Store commands behind the common command interface.Replace the dispatcher branches with command lookup and `execute()`.
Further Reading
- Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software.
- Joshua Kerievsky, Replace Conditional Dispatcher with Command in Refactoring to Patterns.
- Eric Freeman and Elisabeth Robson, Head First Design Patterns, 2nd Edition, Chapter 6.
- Oracle Java documentation: Swing Action usage and UndoableEdit.
References
- (Freeman and Robson 2020): Eric Freeman and Elisabeth Robson (2020) Head First Design Patterns. 2nd ed. O’Reilly Media.
- (Gamma et al. 1995): Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (1995) Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
- (Kerievsky 2004): Joshua Kerievsky (2004) Refactoring to Patterns. Addison-Wesley Professional.
- (Oracle 2026): Oracle (2026) “Uses of Interface javax.swing.Action.”
- (Oracle 2026): Oracle (2026) “Interface UndoableEdit.”