100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Software Design

Command Pattern

The Command pattern turns a request into a standalone object with a uniform execute() method, decoupling the invoker from the receiver and enabling undo, queuing, and logging.

Behavioral Patterns IIntermediate10 min readJul 10, 2026
Analogies

What Is the Command Pattern?

The Command pattern encapsulates a request — an action plus all the parameters needed to perform it — as a standalone object with a uniform execute() method, so the code that triggers an action (the invoker) is decoupled from the code that knows how to perform it (the receiver). This turns operations into first-class objects that can be queued, logged, passed around, or undone, instead of being hardwired as direct method calls.

🏏

Cricket analogy: A DRS review request is a Command object: the on-field umpire (invoker) doesn't need to know how the third umpire (receiver) actually analyzes Hawk-Eye data, it just calls execute() by raising the TV signal.

Structure: Invoker, Command, Receiver

The Command interface declares an execute() method (and often an undo() method); each ConcreteCommand stores a reference to a Receiver plus whatever parameters the action needs, and implements execute() by calling the appropriate method(s) on that receiver. The Invoker holds a Command reference and calls execute() on it without knowing which ConcreteCommand or Receiver is behind it, which is what lets you swap a 'light on' button to control a fan or a garage door just by changing which Command object is bound to it.

🏏

Cricket analogy: A remote-control fielding buzzer (Invoker) just presses a button; the ConcreteCommand behind it knows to signal the twelfth man (Receiver) to bring out gloves, and swapping the buzzer's wiring to instead signal a drinks break changes only the Command, not the buzzer.

typescript
interface Command {
  execute(): void;
  undo(): void;
}

class TextDocument {
  content = '';
  insert(text: string, at: number): void {
    this.content = this.content.slice(0, at) + text + this.content.slice(at);
  }
  delete(at: number, length: number): void {
    this.content = this.content.slice(0, at) + this.content.slice(at + length);
  }
}

class InsertTextCommand implements Command {
  constructor(private doc: TextDocument, private text: string, private at: number) {}
  execute(): void {
    this.doc.insert(this.text, this.at);
  }
  undo(): void {
    this.doc.delete(this.at, this.text.length);
  }
}

class CommandHistory {
  private stack: Command[] = [];
  execute(command: Command): void {
    command.execute();
    this.stack.push(command);
  }
  undoLast(): void {
    const command = this.stack.pop();
    command?.undo();
  }
}

const doc = new TextDocument();
const history = new CommandHistory();
history.execute(new InsertTextCommand(doc, 'Hello', 0));
history.undoLast();

Undo/Redo and Command Queues

Because a Command object captures everything needed to perform an action, it can also capture everything needed to reverse it, which is why text editors, drawing tools, and database migration frameworks implement undo() alongside execute() and keep an executed-commands stack; popping that stack and calling undo() on the top command reverts the last operation cleanly. The same encapsulation lets commands be queued and executed later — asynchronously, in batch, or on a different thread — since a Command object is just data plus a method, with no dependency on the calling context that created it.

🏏

Cricket analogy: A DRS 'umpire's call' reversal works because the review system captured the exact ball-tracking data needed to re-adjudicate, the same way a Command's undo() needs the original state captured at execute() time.

Command's execute() and undo() forming a matched pair is exactly the idea behind database migration frameworks: an up() migration is a ConcreteCommand's execute(), and down() is its undo(), each capturing enough information to precisely reverse the other.

Command vs Strategy

Command and Strategy look structurally similar — both wrap behavior behind a single-method interface — but their intent differs: Strategy is about choosing which algorithm computes a result, and is usually stateless and interchangeable at any time, while Command is about capturing a request as an object, often carrying state specific to that one invocation (like undo history, timestamps, or a queue position), and is meant to be executed once, possibly logged, and possibly reversed.

🏏

Cricket analogy: Choosing between two run-chase calculation methods for required run-rate (Strategy) is stateless math, whereas a specific DRS review request tied to one exact ball bowled at 14.3 overs (Command) is a one-time, loggable, reversible event.

Not every Command needs undo(); a fire-and-forget action like sending an email log entry is a valid Command without a reverse operation. Only add undo() when the domain genuinely requires reversibility — implementing it speculatively adds complexity and state you may never use.

  • Command encapsulates a request as an object with a uniform execute() method, decoupling the invoker from the receiver.
  • The three participants are Invoker, Command (interface plus ConcreteCommand), and Receiver.
  • Because a Command captures the full context of a request, it can also implement undo() to reverse it.
  • An executed-commands stack lets applications support multi-level undo/redo cleanly.
  • Commands are just data plus a method, so they can be queued, logged, or executed asynchronously.
  • Command differs from Strategy in intent: Strategy picks a stateless algorithm, Command captures one discrete, often-reversible invocation.
  • Not every Command needs undo(); add it only when the domain requires reversibility.

Practice what you learned

Was this page helpful?

Topics covered

#SoftwareDesign#DesignPatternsStudyNotes#SoftwareEngineering#CommandPattern#Command#Pattern#Structure#Invoker#StudyNotes#SkillVeris