Command Pattern

Command Pattern

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

Intent

The Command pattern converts a request into a standalone object that contains all information about the request. The object can be stored, queued, scheduled, logged, and replayed — and because it carries the receiver and its parameters, it can be undone. The Invoker that triggers commands needs to know only one thing about them: they have an execute() method.

When NOT to Use

  • A simple direct method call suffices — wrapping every action in a Command object adds class overhead without benefit
  • No requirement for undo/redo, queuing, logging, or remote execution — these are the scenarios that justify the pattern
  • The command hierarchy grows large without reuse — if each command is used exactly once and never queued or undone, the abstraction is premature

When to Use

  • The application needs undo/redo history — each Command can store pre-execution state or an inverse operation
  • Actions must be queued, scheduled for deferred execution, or transmitted over a network
  • A callback (closure) does not carry enough context — Command objects bind receiver, method, and parameters together explicitly
  • Multiple invokers need to trigger the same operation on different receivers without knowing receiver specifics

How It Works

Participants:

  • Command — interface declaring execute() (and optionally undo())
  • ConcreteCommand — binds a specific Receiver instance and a set of parameters; execute() calls the appropriate Receiver method
  • Receiver — the object that performs the actual work (knows how to carry out the operation)
  • Invoker — asks the Command to carry out a request; may hold a history array for undo/replay
  • Client — creates the ConcreteCommand and sets its Receiver; hands the command to the Invoker

The Invoker is entirely decoupled from the Receiver — it knows only that commands have execute(). The ConcreteCommand bridges Invoker and Receiver.

Class Diagram

classDiagram
    class Invoker {
        -history : Command[]
        +executeCommand(cmd : Command)
        +undo()
    }
    class Command {
        <<interface>>
        +execute()*
        +undo()*
    }
    class ConcreteCommandA {
        -receiver : Receiver
        -prevState
        +execute()
        +undo()
    }
    class ConcreteCommandB {
        -receiver : Receiver
        -prevState
        +execute()
        +undo()
    }
    class Receiver {
        +actionA()
        +actionB()
    }
    Invoker o-- Command : stores and invokes
    Command <|.. ConcreteCommandA
    Command <|.. ConcreteCommandB
    ConcreteCommandA --> Receiver : calls
    ConcreteCommandB --> Receiver : calls

    note for Invoker "Tracks command history\nfor undo/redo support"

TypeScript Example

// Command — TypeScript
// Source: GoF Design Patterns, p.233
 
interface Command { execute(): void; }
 
class Light {
  on()  { console.log('Light on');  }
  off() { console.log('Light off'); }
}
 
class LightOnCommand implements Command {
  constructor(private light: Light) {}
  execute() { this.light.on(); }
}
 
class Invoker {
  private history: Command[] = [];
  run(cmd: Command) {
    cmd.execute();
    this.history.push(cmd);   // enables undo / replay
  }
}
 
const invoker = new Invoker();
invoker.run(new LightOnCommand(new Light()));

Java Example

// Command — Java
// Source: GoF Design Patterns, p.233
 
interface Command { void execute(); }
 
class Light {
    public void on()  { System.out.println("Light on");  }
    public void off() { System.out.println("Light off"); }
}
 
class LightOnCommand implements Command {
    private final Light light;
    LightOnCommand(Light light) { this.light = light; }
    public void execute() { light.on(); }
}
 
class Invoker {
    private final java.util.List<Command> history = new java.util.ArrayList<>();
    public void run(Command cmd) {
        cmd.execute();
        history.add(cmd);   // enables undo / replay
    }
}

Lineage Forward

Command → CQRS lineage:

The Command pattern is the upstream ancestor of the CQRS-Pattern (created Phase 12) command-dispatch model. In CQRS, every write operation is a Command object routed through a CommandBus to a handler — the Invoker/ConcreteCommand/Receiver triad maps directly to CommandBus/CommandHandler/Aggregate. The lineage continues: Command → CQRS → Orchestration-Saga-Pattern (Phase 13), where the Saga orchestrator issues Commands to coordinate distributed transactions.

PatternRelationship
Strategy-PatternBoth encapsulate behaviour, but Command encapsulates a specific request with receiver state; Strategy encapsulates an algorithm without binding to a receiver
Memento-PatternCommand + Memento = undo/redo system — Command triggers the action, Memento snapshots state for reversal
Chain-of-Responsibility-PatternCoR dispatches a request along a handler chain; Command encapsulates that request as a first-class object

Sources

  • Gamma, Helm, Johnson, Vlissides — Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994, p.233

Notes that link here: GoF-Patterns-MOC