Back to Design Patterns
Behavioral Pattern

Command Pattern

Encapsulate a request as an object, allowing you to parameterize clients, queue operations, log history, and support undoable actions.

Intermediate11 min read

What is the Command Pattern?

The Command pattern is a behavioral design pattern that turns a request into a standalone object. That object contains everything needed to perform the action — the receiver, the method to call, and any arguments. This lets you treat requests as data: store them, pass them around, queue them, log them, and reverse them.

Think of a restaurant order slip: a waiter doesn't cook the food — they take your order, write it on a slip (the command object), and hand it to the kitchen (the receiver). The slip can be queued, prioritised, or cancelled. The waiter doesn't need to know how to cook.

In software, this is the foundation for undo/redo, transaction rollback, macro recording, task queues, and any feature where actions need to be stored, deferred, or reversed.

Key Idea: Encapsulate actions as objects. This decouples the sender (who triggers the action) from the receiver (who executes it), and enables undo, queuing, and logging.

Core Participants

Command

Command Interface

Declares execute() and (optionally) undo() methods. All concrete commands implement this interface, making them interchangeable from the invoker's perspective.

Concrete Command

Concrete Command

Implements the Command interface. Holds a reference to the Receiver and maps execute()/undo() to specific receiver method calls. Also stores any data needed to reverse the operation.

Receiver

Receiver

The object that knows how to perform the actual work. The concrete command delegates to the receiver. The receiver can be any class — TextEditor, Light, BankAccount, etc.

Invoker

Invoker (Sender)

Stores and triggers commands. It doesn't know anything about the concrete command or receiver — it just calls execute(). The invoker can maintain a history stack for undo/redo.

Client

Client

Creates concrete command objects, sets their receivers, and gives them to the invoker. The client wires everything together but doesn't execute commands directly.

!Problem It Solves

Tight Sender-Receiver Coupling

Without Command, the sender (e.g., a button) directly calls the receiver's method. This means:

  • → UI buttons are coupled to business logic — hard to reuse
  • → No way to implement undo without custom code everywhere
  • → No way to queue, schedule, or defer actions
  • → Macro recording (replay a sequence of actions) is impossible

Without Command

// Button directly calls receiver — tightly coupled
class SaveButton {
    void onClick() {
        editor.save();  // Coupled! No undo, no queuing
    }
}
class UndoButton {
    void onClick() {
        // Where does undo history live? Who tracks it?
        // No standard mechanism — must reinvent every time
    }
}

With Command

// Button just invokes a command — completely decoupled
saveButton.setCommand(new SaveCommand(editor));
undoButton.setCommand(() -> history.undo());
// Undo is automatic — history tracks all commands

Implementing Undo / Redo

Undo/Redo is the killer feature of the Command pattern. Here's how it works with two stacks:

Execute

Run the command and push it onto the undo stack. Clear the redo stack (new action invalidates redo history).

Undo

Pop from the undo stack, call undo(), push to redo stack. Each command is responsible for reversing its own action.

Redo

Pop from the redo stack, call execute() again, push back to undo stack. No special logic needed.

Undo / Redo Stack Flow

Cmd A
Cmd B
Cmd C
← Undo Stack
|
Redo Stack →
Cmd C (undone)

Implementation

// Command Pattern — Text Editor with Undo/Redo

import java.util.Stack;

// ─── Command interface ────────────────────────────────────────
interface Command {
    void execute();
    void undo();
}

// ─── Receiver — the object that does the actual work ──────────
class TextEditor {
    private StringBuilder text = new StringBuilder();

    public void insertText(int pos, String content) {
        text.insert(pos, content);
        System.out.println("[Editor] Text: "" + text + """);
    }

    public void deleteText(int pos, int length) {
        text.delete(pos, pos + length);
        System.out.println("[Editor] Text: "" + text + """);
    }

    public String getText() { return text.toString(); }
}

// ─── Concrete Commands ────────────────────────────────────────
class InsertCommand implements Command {
    private TextEditor editor;
    private int position;
    private String content;

    public InsertCommand(TextEditor editor, int position, String content) {
        this.editor   = editor;
        this.position = position;
        this.content  = content;
    }

    @Override
    public void execute() {
        editor.insertText(position, content);
    }

    @Override
    public void undo() {
        // Reverse: delete what was just inserted
        editor.deleteText(position, content.length());
    }
}

class DeleteCommand implements Command {
    private TextEditor editor;
    private int position;
    private int length;
    private String deletedText;  // saved for undo

    public DeleteCommand(TextEditor editor, int position, int length) {
        this.editor   = editor;
        this.position = position;
        this.length   = length;
    }

    @Override
    public void execute() {
        // Save deleted content before removing it
        deletedText = editor.getText().substring(position, position + length);
        editor.deleteText(position, length);
    }

    @Override
    public void undo() {
        editor.insertText(position, deletedText);
    }
}

// ─── Invoker — manages command history and execution ──────────
class CommandHistory {
    private Stack<Command> undoStack = new Stack<>();
    private Stack<Command> redoStack = new Stack<>();

    public void execute(Command command) {
        command.execute();
        undoStack.push(command);
        redoStack.clear();  // new action clears redo history
    }

    public void undo() {
        if (!undoStack.isEmpty()) {
            Command command = undoStack.pop();
            command.undo();
            redoStack.push(command);
            System.out.println("[History] Undo performed");
        }
    }

    public void redo() {
        if (!redoStack.isEmpty()) {
            Command command = redoStack.pop();
            command.execute();
            undoStack.push(command);
            System.out.println("[History] Redo performed");
        }
    }
}

// ─── Client ───────────────────────────────────────────────────
public class Application {
    public static void main(String[] args) {
        TextEditor editor   = new TextEditor();
        CommandHistory history = new CommandHistory();

        history.execute(new InsertCommand(editor, 0, "Hello"));
        history.execute(new InsertCommand(editor, 5, " World"));
        history.execute(new DeleteCommand(editor, 0, 5));
        // Text: " World"

        history.undo();  // Restores "Hello World"
        history.undo();  // Restores "Hello"
        history.redo();  // Re-applies " World" → "Hello World"
    }
}

When to Use Command

Undo / Redo

Any editor or interactive application that needs history — text editors, drawing apps, IDEs, spreadsheets.

TextEditor, PhotoEditor, SpreadsheetApp

Transaction Rollback

Database-style operations where a failure mid-way should undo all previous steps atomically.

BankTransfer, OrderPlacement, BatchUpdate

Task Queues & Scheduling

When operations need to be queued, deferred, or executed at a specific time rather than immediately.

JobQueue, TaskScheduler, BackgroundWorker

Macro Recording

Record a sequence of user actions and replay them — used in automation, testing, and productivity tools.

MacroRecorder, TestPlayback, ScriptBuilder

GUI Buttons & Shortcuts

Decouple UI controls from business logic — a button, keyboard shortcut, and menu item all trigger the same command object.

SaveCommand, CopyCommand, PasteCommand

Pros & Cons

Advantages

  • Decouples sender from receiver — Single Responsibility
  • Undo/Redo comes for free with a history stack
  • Commands can be queued, scheduled, or executed remotely
  • Composite commands (Macro) combine multiple actions into one
  • Easy to add new commands without changing existing code

Disadvantages

  • More classes — one per command type
  • Simple use cases can feel over-engineered
  • State management for undo can get complex (deep copies needed)
  • Long command histories consume memory

Real-World Examples

Git Commits

Every git commit is a Command object — it records the change (execute) and knows how to reverse itself (git revert). Git's entire history model is built on Command.

Database Transactions (SQL)

BEGIN, COMMIT, ROLLBACK is a Command pattern implementation. Each statement is a command that can be committed (execute) or rolled back (undo) atomically.

Redux Actions (JavaScript)

Redux actions are Command objects — plain JS objects describing what happened. The reducer's switch-case maps each action to a state transformation. Time-travel debugging is undo/redo via Command.

Java's Runnable / Callable

Runnable and Callable are the Command pattern. They encapsulate work to be done and are passed to an Executor (the invoker) for queuing and execution on a thread pool.

What's Next?

Now that you understand Command, explore related Behavioral patterns: