Behavioral

Command

Encapsulate a request as an object — enabling undo/redo, queuing, logging, and parameterisation of operations.

Intermediate Pattern #14 of 23

What is the Command Pattern?

Command turns a request into a standalone object. This object contains the action and all information needed to execute it. You can store commands, pass them as arguments, queue them, log them, and reverse them — none of which is possible with a simple method call.

The Invoker triggers execution without knowing anything about the Receiver. The Client creates the Command object connecting Invoker and Receiver.

Real-world analogy: A restaurant order slip. The waiter (Invoker) writes down your order (Command) and passes it to the kitchen (Receiver). The waiter doesn't know how to cook — the slip contains all information. Orders can be queued, cancelled, or recalled.

The Problem It Solves

You're building a text editor with Undo/Redo. When the user types, cuts, or pastes, you need to remember what happened and reverse it. Storing operations as method calls is impossible — you can't "unsee" a method call or put it in a queue.

Command wraps every operation in an object with execute() and undo(). You push executed commands onto a stack. Undo pops and calls undo(). Redo replays from another stack.

Participants

Command (interface)
Declares execute(). Often also declares undo() for reversible commands.
ConcreteCommand
Binds a Receiver with the action. Stores state needed to undo. Implements execute() by calling methods on the Receiver.
Receiver
The object that knows how to perform the actual work. ConcreteCommand calls its methods.
Invoker
Triggers commands. Holds a command reference. Never calls receiver directly. May maintain a command history for undo/redo.
Client
Creates ConcreteCommand objects, sets their Receiver, and passes them to the Invoker.

Visual Flow Diagram

Client creates cmd Invoker cmd: Command history: Stack executeCmd() «interface» Command execute() / undo() ConcreteCommand receiver: Receiver execute() → receiver.do() Receiver action() calls

Java Code Example

Java — Text Editor with Undo/Redo
// Command interface with undo support
public interface Command {
    void execute();
    void undo();
}

// Receiver — knows how to do the actual work
public class TextEditor {
    private StringBuilder text = new StringBuilder();

    public void insertText(String s, int pos) { text.insert(pos, s); }
    public void deleteText(int start, int len)  { text.delete(start, start+len); }
    public String getText()                        { return text.toString(); }
}

// ConcreteCommand — Insert operation with undo
public class InsertCommand implements Command {
    private final TextEditor editor;
    private final String     text;
    private final int        position;

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

    public void execute() { editor.insertText(text, position); }
    public void undo()    { editor.deleteText(position, text.length()); }
}

// ConcreteCommand — Delete operation with undo
public class DeleteCommand implements Command {
    private final TextEditor editor;
    private final int         start, length;
    private String            backup; // saved for undo

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

    public void execute() {
        backup = editor.getText().substring(start, start+length); // save
        editor.deleteText(start, length);
    }
    public void undo() { editor.insertText(backup, start); } // restore
}

// Invoker — maintains command history for undo/redo
public class CommandHistory {
    private final Deque<Command> undoStack = new ArrayDeque<>();
    private final Deque<Command> redoStack = new ArrayDeque<>();

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

    public void undo() {
        if (!undoStack.isEmpty()) {
            Command cmd = undoStack.pop();
            cmd.undo();
            redoStack.push(cmd);
        }
    }

    public void redo() {
        if (!redoStack.isEmpty()) {
            Command cmd = redoStack.pop();
            cmd.execute();
            undoStack.push(cmd);
        }
    }
}

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

        history.execute(new InsertCommand(editor, "Hello", 0));
        history.execute(new InsertCommand(editor, " World", 5));
        System.out.println(editor.getText()); // Hello World

        history.undo();
        System.out.println(editor.getText()); // Hello

        history.redo();
        System.out.println(editor.getText()); // Hello World
    }
}

When to Use / Avoid

✓ Use When

  • You need undo/redo functionality
  • Operations need to be queued, scheduled, or executed at a different time
  • You want to log operations for auditing or replay
  • You need to parameterise UI elements (buttons, menus) with operations
  • Transactional behaviour — rollback a series of commands on failure

✕ Avoid When

  • Operations are simple and undo/queuing/logging are not needed
  • The number of command classes becomes unmanageable — consider lambdas
  • Memory is constrained — storing command history for undo can be large

Real-World Examples

Pros & Cons

Pros

  • Single Responsibility — decouples operation triggering from execution
  • Open/Closed — add new commands without changing Invoker or Receiver
  • Undo/Redo, queuing, logging all become natural
  • Compose macro commands from simpler ones

Cons

  • Class explosion — one class per operation can become unwieldy
  • Undo history consumes memory for complex object state snapshots
  • In modern Java, lambdas/method references often replace simple Commands

How Command Can Be Broken

⚠ Attack Vectors

  • Incomplete undo: execute() makes side effects that undo() doesn't reverse — partial state restoration causes corruption on undo
  • Not saving state before execute: DeleteCommand deletes text but forgets to save it in execute()undo() has nothing to restore from
  • execute() called multiple times: The same command object is re-executed without resetting state — history stacks get misaligned
  • Undo past origin: User undoes more times than commands in history — empty stack pop causes NoSuchElementException
  • Mutable receiver state captured at wrong time: Command captures a reference to a mutable object — by the time execute() runs, the object's state has changed

✓ Prevention

  • Save all state in execute() before modifying: The first thing execute() does is snapshot every piece of state it will change — undo() restores from that snapshot
  • One-shot commands: Mark a command as "used" after execute() — throw if re-executed to prevent double execution bugs
  • Guard stack pops: Always check !stack.isEmpty() before popping in undo()/redo() — or use peek() first
  • Capture value, not reference: Copy primitive values and immutable state into the command object at construction time — not a reference that can change later
  • MacroCommand for transactions: Wrap multiple commands in a MacroCommand — if any sub-command fails, undo all previously executed ones in reverse order
Java — Break & Fix
// ❌ BREAKING — state not saved before execute
public class BrokenDeleteCommand implements Command {
    private String backup; // never populated!
    public void execute() { editor.deleteText(start, len); } // ❌ no save
    public void undo()    { editor.insertText(backup, start); } // ❌ backup=null
}

// ✅ FIX — save state in execute() first
public void execute() {
    backup = editor.getText().substring(start, start+len); // ✅ save first
    editor.deleteText(start, len);
}

// ❌ BREAKING — double execution corrupts history
Command cmd = new InsertCommand(editor, "Hi", 0);
history.execute(cmd);
history.execute(cmd); // ❌ same object — history has two refs, undo misaligns

// ✅ FIX — one-shot guard
private boolean executed = false;
public void execute() {
    if (executed) throw new IllegalStateException("Command already executed");
    executed = true;
    editor.insertText(text, position);
}

// ✅ MacroCommand — all-or-nothing transaction
public class MacroCommand implements Command {
    private final List<Command> commands;
    private final Deque<Command> executed = new ArrayDeque<>();

    public void execute() {
        for (Command cmd : commands) {
            try {
                cmd.execute();
                executed.push(cmd);
            } catch (Exception e) {
                undo(); // rollback all executed so far
                throw e;
            }
        }
    }

    public void undo() {
        while (!executed.isEmpty()) executed.pop().undo(); // reverse order
    }
}

How Other Patterns Relate

Interview Cheat Sheet

  1. What: Wraps a request in an object with execute() and undo(). Decouples the Invoker (who triggers) from the Receiver (who does the work). Enables undo/redo, queuing, logging, and transactions.
  2. How: Command interface with execute()/undo(). ConcreteCommand holds Receiver reference + state snapshot. Invoker maintains undo/redo stacks. Client wires Command(Receiver) and passes to Invoker.
  3. Modern Java: Simple commands without undo can be replaced by lambdas (Runnable, Callable). Use full Command class only when undo, queuing, or serialisation are required.