What is the Command Pattern?

The Command Pattern is a behavioral design pattern that encapsulates a request as an object. This lets you parameterize operations, queue them, log them, and support undo/redo functionality. The invoker (who triggers the command) is decoupled from the receiver (who executes the action).

Explanation

Real-World Analogy

  • Think of a restaurant order 🍽️:
    • Customer → places order (client creates the command)
    • Order slip → is the command object (encapsulates the request)
    • Waiter → takes the order to the kitchen (invoker — doesn’t know how to cook)
    • Chef → executes the cooking (receiver — does the actual work)
  • The waiter doesn’t need to know how to cook. The order slip can be queued, cancelled, or reordered.

Participants

RoleResponsibility
Command (interface)Declares execute() and undo()
Concrete CommandImplements execute/undo; holds reference to receiver
ReceiverKnows how to perform the actual work
InvokerCalls execute() on commands; may maintain history
ClientCreates command objects, sets up receiver and invoker

UML Structure

classDiagram
    class Invoker {
        -history: List~Command~
        +execute(cmd: Command)
        +undo()
    }
    class Command {
        <<interface>>
        +execute()
        +undo()
    }
    class ConcreteCommand {
        -receiver: Receiver
        +execute()
        +undo()
    }
    class Receiver {
        +action()
    }
    Invoker --> Command
    Command <|.. ConcreteCommand
    ConcreteCommand --> Receiver

Implementation

  • A text editor with InsertText, DeleteText, BoldText commands — with full undo/redo stack. Languages: Python · Cpp · Java · Java Script · CSharp

# ─── Python — Text Editor with undo/redo ─────────────────────────────
from abc import ABC, abstractmethod
from collections import deque
 
# ── Command interface ──────────────────────────────────
class Command(ABC):
    @abstractmethod
    def execute(self) -> None: ...
 
    @abstractmethod
    def undo(self) -> None: ...
 
    @abstractmethod
    def description(self) -> str: ...
 
# ── Receiver — the actual text document ───────────────
class TextDocument:
    def __init__(self):
        self._content = ""
 
    def insert(self, text: str, position: int = -1) -> None:
        if position == -1 or position >= len(self._content):
            self._content += text
        else:
            self._content = self._content[:position] + text + self._content[position:]
 
    def delete(self, start: int, end: int) -> str:
        deleted = self._content[start:end]
        self._content = self._content[:start] + self._content[end:]
        return deleted
 
    @property
    def content(self) -> str:
        return self._content
 
    def __repr__(self) -> str:
        return f"Document({self._content!r})"
 
# ── Concrete Commands ──────────────────────────────────
class InsertCommand(Command):
    def __init__(self, doc: TextDocument, text: str, position: int = -1):
        self.doc = doc
        self.text = text
        self.position = position
 
    def execute(self) -> None:
        self.doc.insert(self.text, self.position)
 
    def undo(self) -> None:
        actual_pos = self.position if self.position != -1 else len(self.doc.content) - len(self.text)
        self.doc.delete(actual_pos, actual_pos + len(self.text))
 
    def description(self) -> str:
        return f"Insert({self.text!r} at {self.position})"
 
class DeleteCommand(Command):
    def __init__(self, doc: TextDocument, start: int, end: int):
        self.doc = doc
        self.start = start
        self.end = end
        self._deleted = ""
 
    def execute(self) -> None:
        self._deleted = self.doc.delete(self.start, self.end)
 
    def undo(self) -> None:
        self.doc.insert(self._deleted, self.start)
 
    def description(self) -> str:
        return f"Delete([{self.start}:{self.end}])"
 
class MacroCommand(Command):
    """Composite command — executes multiple commands as one."""
    def __init__(self, name: str, *commands: Command):
        self.name = name
        self.commands = list(commands)
 
    def execute(self) -> None:
        for cmd in self.commands: cmd.execute()
 
    def undo(self) -> None:
        for cmd in reversed(self.commands): cmd.undo()
 
    def description(self) -> str: return f"Macro({self.name})"
 
# ── Invoker — command history manager ─────────────────
class Editor:
    def __init__(self, doc: TextDocument):
        self.doc = doc
        self._history: list[Command] = []
        self._redo_stack: list[Command] = []
 
    def execute(self, command: Command) -> None:
        command.execute()
        self._history.append(command)
        self._redo_stack.clear()  # redo stack invalidated on new action
        print(f"  ✅ {command.description()}{self.doc.content!r}")
 
    def undo(self) -> None:
        if not self._history:
            print("  ⚠️ Nothing to undo")
            return
        cmd = self._history.pop()
        cmd.undo()
        self._redo_stack.append(cmd)
        print(f"  ↩️ Undid: {cmd.description()}{self.doc.content!r}")
 
    def redo(self) -> None:
        if not self._redo_stack:
            print("  ⚠️ Nothing to redo")
            return
        cmd = self._redo_stack.pop()
        cmd.execute()
        self._history.append(cmd)
        print(f"  ↪️ Redid: {cmd.description()}{self.doc.content!r}")
 
# Usage
doc = TextDocument()
editor = Editor(doc)
 
print("--- Executing commands ---")
editor.execute(InsertCommand(doc, "Hello"))
editor.execute(InsertCommand(doc, " World"))
editor.execute(InsertCommand(doc, "!"))
editor.execute(DeleteCommand(doc, 5, 11))  # delete " World"
 
print("\n--- Undo operations ---")
editor.undo()  # undo delete
editor.undo()  # undo "!"
 
print("\n--- Redo ---")
editor.redo()  # redo "!"
 
print("\n--- Macro command ---")
macro = MacroCommand("greet",
    InsertCommand(doc, " Beautiful"),
    InsertCommand(doc, " Day"))
editor.execute(macro)
editor.undo()  # undo the entire macro at once
// ─── C++ — Command Pattern with undo ─────────────────────────────────
#include <iostream>
#include <string>
#include <vector>
#include <memory>
 
class Command {
public:
    virtual void execute() = 0;
    virtual void undo() = 0;
    virtual std::string description() const = 0;
    virtual ~Command() {}
};
 
class TextDocument {
    std::string content_;
public:
    void append(const std::string& text) { content_ += text; }
    void remove(int n) { if (n <= (int)content_.size()) content_.erase(content_.size() - n); }
    std::string getContent() const { return content_; }
};
 
class InsertCommand : public Command {
    TextDocument& doc_;
    std::string text_;
public:
    InsertCommand(TextDocument& d, std::string t) : doc_(d), text_(t) {}
    void execute() override { doc_.append(text_); }
    void undo() override { doc_.remove(text_.size()); }
    std::string description() const override { return "Insert(\"" + text_ + "\")"; }
};
 
class Editor {
    std::vector<std::unique_ptr<Command>> history_;
public:
    void execute(std::unique_ptr<Command> cmd) {
        cmd->execute();
        std::cout << "  ✅ " << cmd->description() << "\n";
        history_.push_back(std::move(cmd));
    }
    void undo() {
        if (history_.empty()) { std::cout << "  Nothing to undo\n"; return; }
        history_.back()->undo();
        std::cout << "  ↩️ Undid: " << history_.back()->description() << "\n";
        history_.pop_back();
    }
};
 
int main() {
    TextDocument doc;
    Editor editor;
    editor.execute(std::make_unique<InsertCommand>(doc, "Hello"));
    editor.execute(std::make_unique<InsertCommand>(doc, " World"));
    std::cout << "Content: " << doc.getContent() << "\n";
    editor.undo();
    std::cout << "After undo: " << doc.getContent() << "\n";
}
// ─── Java — Command Pattern ───────────────────────────────────────────
import java.util.*;
 
interface Command {
    void execute();
    void undo();
    String description();
}
 
class TextDocument {
    StringBuilder content = new StringBuilder();
    void append(String text) { content.append(text); }
    void remove(int n) { if (n <= content.length()) content.delete(content.length() - n, content.length()); }
    String getContent() { return content.toString(); }
}
 
class InsertCommand implements Command {
    TextDocument doc; String text;
    InsertCommand(TextDocument d, String t) { this.doc = d; this.text = t; }
    public void execute() { doc.append(text); }
    public void undo() { doc.remove(text.length()); }
    public String description() { return "Insert(\"" + text + "\")"; }
}
 
class Editor {
    Deque<Command> history = new ArrayDeque<>();
    public void execute(Command cmd) {
        cmd.execute();
        history.push(cmd);
        System.out.println("  ✅ " + cmd.description());
    }
    public void undo() {
        if (history.isEmpty()) { System.out.println("  Nothing to undo"); return; }
        Command cmd = history.pop();
        cmd.undo();
        System.out.println("  ↩️ Undid: " + cmd.description());
    }
}
 
class CommandDemo {
    public static void main(String[] args) {
        TextDocument doc = new TextDocument();
        Editor editor = new Editor();
        editor.execute(new InsertCommand(doc, "Hello"));
        editor.execute(new InsertCommand(doc, " World"));
        System.out.println("Content: " + doc.getContent());
        editor.undo();
        System.out.println("After undo: " + doc.getContent());
    }
}
// ─── JavaScript — Command Pattern ─────────────────────────────────────
class TextDocument {
    constructor() { this._content = ""; }
    append(text) { this._content += text; }
    remove(n) { this._content = this._content.slice(0, -n); }
    get content() { return this._content; }
}
 
class InsertCommand {
    constructor(doc, text) { this.doc = doc; this.text = text; }
    execute() { this.doc.append(this.text); }
    undo() { this.doc.remove(this.text.length); }
    get description() { return `Insert("${this.text}")`; }
}
 
class Editor {
    #history = [];
    #redoStack = [];
 
    execute(cmd) {
        cmd.execute();
        this.#history.push(cmd);
        this.#redoStack = [];
        console.log(`  ✅ ${cmd.description}`);
    }
    undo() {
        if (!this.#history.length) { console.log("  Nothing to undo"); return; }
        const cmd = this.#history.pop();
        cmd.undo();
        this.#redoStack.push(cmd);
        console.log(`  ↩️ Undid: ${cmd.description}`);
    }
    redo() {
        if (!this.#redoStack.length) { console.log("  Nothing to redo"); return; }
        const cmd = this.#redoStack.pop();
        cmd.execute();
        this.#history.push(cmd);
        console.log(`  ↪️ Redid: ${cmd.description}`);
    }
}
 
const doc = new TextDocument();
const editor = new Editor();
editor.execute(new InsertCommand(doc, "Hello"));
editor.execute(new InsertCommand(doc, " World"));
console.log("Content:", doc.content);
editor.undo();
console.log("After undo:", doc.content);
editor.redo();
console.log("After redo:", doc.content);
// ─── C# — Command Pattern with undo/redo ─────────────────────────────
using System;
using System.Collections.Generic;
using System.Text;
 
interface ICommand {
    void Execute();
    void Undo();
    string Description { get; }
}
 
class TextDocument {
    StringBuilder content = new();
    public void Append(string text) => content.Append(text);
    public void Remove(int n) { if (n <= content.Length) content.Remove(content.Length - n, n); }
    public string Content => content.ToString();
}
 
class InsertCommand : ICommand {
    TextDocument doc; string text;
    public InsertCommand(TextDocument d, string t) { doc = d; text = t; }
    public void Execute() => doc.Append(text);
    public void Undo() => doc.Remove(text.Length);
    public string Description => $"Insert(\"{text}\")";
}
 
class Editor {
    Stack<ICommand> history = new();
    Stack<ICommand> redo = new();
 
    public void Execute(ICommand cmd) {
        cmd.Execute();
        history.Push(cmd);
        redo.Clear();
        Console.WriteLine($"  ✅ {cmd.Description}");
    }
    public void Undo() {
        if (history.Count == 0) { Console.WriteLine("  Nothing to undo"); return; }
        var cmd = history.Pop();
        cmd.Undo();
        redo.Push(cmd);
        Console.WriteLine($"  ↩️ Undid: {cmd.Description}");
    }
    public void Redo() {
        if (redo.Count == 0) { Console.WriteLine("  Nothing to redo"); return; }
        var cmd = redo.Pop();
        cmd.Execute();
        history.Push(cmd);
        Console.WriteLine($"  ↪️ Redid: {cmd.Description}");
    }
 
    static void Main() {
        var doc = new TextDocument();
        var editor = new Editor();
        editor.Execute(new InsertCommand(doc, "Hello"));
        editor.Execute(new InsertCommand(doc, " World"));
        Console.WriteLine("Content: " + doc.Content);
        editor.Undo();
        Console.WriteLine("After undo: " + doc.Content);
        editor.Redo();
        Console.WriteLine("After redo: " + doc.Content);
    }
}

Key Takeaways

  • Encapsulate requests as objects — enables queuing, logging, and undo/redo.
  • Decouples invoker (who triggers) from receiver (who acts).
  • Undo/redo = maintain a history stack of commands + call undo() in reverse.
  • Macro command = composite command — executes multiple commands as one unit.
  • Real-world uses: text editors, transaction systems, game action queues, UI button actions, HTTP request pipelines.

More Learn

GitHub & Webs