2 min read

Command Pattern

The Command Pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you parameterize methods with different requests, delay or queue a request's execution, and support undoable operations.

1. The Core Concept

The pattern decouples the object that invokes the operation (Sender/Invoker) from the one that knows how to perform it (Receiver).

  • Command: An object representing a specific action. It wraps the method call and its parameters.
  • Invoker: The object that triggers the command (e.g., a Button). It doesn't know what the command does, just that it can execute() it.
  • Receiver: The object that actually does the work (e.g., the Light bulb).

2. Analogy: Restaurant Order

  1. You (Client) give an order to the Waiter (Invoker).
  2. The Waiter writes the order on a Check (Command).
  3. The Waiter places the check in the kitchen queue.
  4. The Chef (Receiver) picks up the check and cooks the meal.

The Waiter doesn't need to know how to cook. The Chef doesn't need to know who ordered. The Check isolates them.

3. Example (TypeScript)

Let's build a remote control for a smart home.

// The Command Interface
interface Command {
    execute(): void;
    undo(): void;
}

// The Receiver
class Light {
    turnOn() { console.log("Light is ON"); }
    turnOff() { console.log("Light is OFF"); }
}

// Concrete Command 1
class TurnOnLightCommand implements Command {
    private light: Light;

    constructor(light: Light) {
        this.light = light;
    }

    execute() {
        this.light.turnOn();
    }

    undo() {
        this.light.turnOff();
    }
}

// The Invoker
class RemoteControl {
    private command: Command;

    setCommand(command: Command) {
        this.command = command;
    }

    pressButton() {
        this.command.execute();
    }

    pressUndo() {
        this.command.undo();
    }
}

// Usage
const light = new Light();
const turnOn = new TurnOnLightCommand(light);

const remote = new RemoteControl();

remote.setCommand(turnOn);
remote.pressButton(); // Output: Light is ON

remote.pressUndo();   // Output: Light is OFF

4. Pros and Cons

Pros Cons
Decoupling: Removes direct dependency between the invoker and the receiver. Complexity: Introduces a lot of new classes (a class for every action).
Undo/Redo: Easy to implement undo/redo functionality.
Queueing: Commands can be serialized, queued, or delayed.
Composite Commands: You can assemble simple commands into complex ones (Macros).

programming/design-patterns programming/object-oriented-programming