🔄 Behavioral

Command Pattern

Encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Command turns method calls into standalone objects that contain all the information needed to perform an action.

Problem

You need to issue requests to objects without knowing anything about the operation being requested or the receiver. You also want to support undo/redo, queuing operations, scheduling execution, or logging command history. Directly coupling the sender to the receiver's methods makes all of these capabilities difficult to implement.

Solution

Create a command interface with an execute method. Each concrete command stores the receiver and the parameters needed for the action. The sender (invoker) triggers the command without knowing what it does. Commands can be stored in a history stack for undo/redo, serialized for logging, or queued for deferred execution.

Participants

RoleResponsibility
CommandDeclares an interface for executing an operation
ConcreteCommandBinds a Receiver to an action; implements execute by invoking operations on the Receiver
InvokerAsks the command to carry out the request
ReceiverKnows how to perform the operations; any class can serve as a receiver

TypeScript Example

TypeScript
interface Command {
  execute(): void;
  undo(): void;
  description: string;
}

class TextEditor {
  content = "";

  insert(text: string, pos: number) {
    this.content = this.content.slice(0, pos) + text + this.content.slice(pos);
  }

  delete(pos: number, length: number): string {
    const deleted = this.content.slice(pos, pos + length);
    this.content = this.content.slice(0, pos) + this.content.slice(pos + length);
    return deleted;
  }
}

class InsertCommand implements Command {
  description: string;
  constructor(private editor: TextEditor, private text: string, private pos: number) {
    this.description = `Insert "${text}" at ${pos}`;
  }
  execute() { this.editor.insert(this.text, this.pos); }
  undo() { this.editor.delete(this.pos, this.text.length); }
}

class CommandHistory {
  private history: Command[] = [];

  execute(cmd: Command) {
    cmd.execute();
    this.history.push(cmd);
  }

  undo(): string | undefined {
    const cmd = this.history.pop();
    if (cmd) { cmd.undo(); return cmd.description; }
  }
}

const editor = new TextEditor();
const history = new CommandHistory();

history.execute(new InsertCommand(editor, "Hello", 0));
history.execute(new InsertCommand(editor, " World", 5));
console.log(editor.content); // "Hello World"

history.undo();
console.log(editor.content); // "Hello"

Real-World Example

Text editors universally use the Command pattern for undo/redo—each keystroke, formatting change, or paste operation is a command object stored in a history stack. Database transactions wrap SQL statements as commands. Task queues like BullMQ or Celery serialize commands for later execution. GUI buttons map user actions to command objects, decoupling the UI from the business logic.

When to Use

  • You need to parameterize objects with actions to perform
  • You want to queue, schedule, or execute operations at different times
  • You need to support undo/redo functionality
  • You want to structure a system around high-level operations built from primitive operations (transactions)

When Not to Use

  • The operations are simple and don't require undo, queuing, or logging
  • Creating a command object for every trivial operation adds unnecessary complexity
  • The system has strict real-time requirements and the overhead of command objects matters

Understanding the Command Pattern

The Command pattern is one of the most important behavioral design patterns in object-oriented software design. Originally cataloged by the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) in their seminal 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software," this pattern continues to be a cornerstone of modern software architecture.

Encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Command turns method calls into standalone objects that contain all the information needed to perform an action. The pattern involves the following key participants: Command, ConcreteCommand, Invoker, Receiver. Each participant has a well-defined role, and the interactions between them create a flexible, maintainable structure that can evolve with changing requirements.

In TypeScript and modern JavaScript development, the Command pattern is particularly valuable because the language's type system and interface support allow you to express the pattern with compile-time safety. TypeScript generics can make Command implementations even more flexible and reusable while maintaining strong type checking throughout the pattern's structure.

Knowing when to apply the Command pattern is as important as knowing how. Common use cases include: You need to parameterize objects with actions to perform; You want to queue, schedule, or execute operations at different times; You need to support undo/redo functionality. However, overusing design patterns can lead to unnecessarily complex code. Always evaluate whether the pattern genuinely solves your problem or if a simpler approach would suffice.

The Command pattern is related to several other design patterns: Memento, Observer, Strategy, Chain of Responsibility, Visitor. Understanding these relationships helps you choose the right pattern for your specific situation and combine patterns effectively when building complex systems.

Related Patterns

More Behavioral Patterns

Explore All Design Patterns

Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.