Design Patterns - Behavioral

Command Pattern

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

12 min readEssential

1What is the Command Pattern?

Real-World Analogy
Restaurant Order

Customer tells waiter their order. Waiter writes it on a slip (Command object) and places it in the kitchen queue. The chef (Receiver) executes the order. The slip can be cancelled or modified before execution.

Remote Control

Each button on a remote is a Command. Pressing "On" creates a TurnOnCommand. The TV (Receiver) executes it. The remote does not know how to turn on the TV - it just invokes the command.

Command Pattern turns a request into a stand-alone object containing all information about the request. This allows you to: parameterize methods with different requests, delay or queue execution, and implement undo/redo operations.

Command Pattern Structure

Client
Creates command
Command
execute(), undo()
Invoker
Triggers command
Receiver
Does actual work

2Why Use Command Pattern

Undo/Redo Support
Store executed commands in history. Call undo() to reverse operations.
Request Queuing
Queue commands for delayed execution, batch processing, or scheduling.
Logging and Auditing
Log all commands for debugging, auditing, or replaying operations.
Decoupling
Invoker is decoupled from receiver. Can swap commands without changing invoker.

3Implementation

Text Editor with Undo/Redo support using Command pattern:

Text Editor with Undo/Redo
// ========== Command Interface ==========
interface Command {
    void execute();
    void undo();
}

// ========== Receiver - The actual text editor ==========
class TextEditor {
    private StringBuilder text = new StringBuilder();

    public void write(String content) {
        text.append(content);
    }

    public void deleteLast(int length) {
        int start = text.length() - length;
        if (start >= 0) {
            text.delete(start, text.length());
        }
    }

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

// ========== Concrete Commands ==========
class WriteCommand implements Command {
    private TextEditor editor;
    private String content;

    public WriteCommand(TextEditor editor, String content) {
        this.editor = editor;
        this.content = content;
    }

    @Override
    public void execute() {
        editor.write(content);
    }

    @Override
    public void undo() {
        editor.deleteLast(content.length());
    }
}

class DeleteCommand implements Command {
    private TextEditor editor;
    private String deletedContent;
    private int length;

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

    @Override
    public void execute() {
        String text = editor.getText();
        int start = Math.max(0, text.length() - length);
        deletedContent = text.substring(start);
        editor.deleteLast(length);
    }

    @Override
    public void undo() {
        editor.write(deletedContent);
    }
}

// ========== Invoker - Manages command history ==========
class EditorInvoker {
    private Stack<Command> history = new Stack<>();
    private Stack<Command> redoStack = new Stack<>();

    public void execute(Command command) {
        command.execute();
        history.push(command);
        redoStack.clear();  // Clear redo after new command
    }

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

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

// ========== Usage ==========
public class Main {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        EditorInvoker invoker = new EditorInvoker();

        // Write some text
        invoker.execute(new WriteCommand(editor, "Hello"));
        System.out.println(editor.getText());  // Hello

        invoker.execute(new WriteCommand(editor, " World"));
        System.out.println(editor.getText());  // Hello World

        // Undo last write
        invoker.undo();
        System.out.println(editor.getText());  // Hello

        // Redo
        invoker.redo();
        System.out.println(editor.getText());  // Hello World

        // Delete 5 characters
        invoker.execute(new DeleteCommand(editor, 5));
        System.out.println(editor.getText());  // Hello 

        // Undo delete
        invoker.undo();
        System.out.println(editor.getText());  // Hello World
    }
}
Output:
Hello
Hello World
Hello
Hello World
Hello 
Hello World

4Real-World Examples

GUI ActionsMenu items, toolbar buttons as commands
TransactionsDatabase transactions with commit/rollback
Task QueuesJob schedulers, message queues
MacrosRecord and playback sequences of commands
ReduxActions are command objects dispatched to store

5Interview Questions

What is the Command pattern and when would you use it?
Command encapsulates a request as an object with all its parameters. Use it when: 1) You need undo/redo functionality, 2) You want to queue or log requests, 3) You need to parameterize objects with operations, 4) You want to decouple invoker from receiver.
How does Command pattern enable undo/redo?
Each Command stores the information needed to reverse its operation. Commands are pushed to a history stack. To undo, pop the last command and call its undo() method, which reverses the operation. For redo, maintain a separate redo stack.
What are the participants in Command pattern?
1) Command: Interface with execute() and optionally undo(). 2) ConcreteCommand: Implements Command, binds receiver to action. 3) Invoker: Asks command to execute. 4) Receiver: Knows how to perform the actual work. 5) Client: Creates commands and sets their receivers.

6Key Takeaways

1Command encapsulates request as object with execute() and undo().
2Enables undo/redo by storing command history.
3Decouples invoker from receiver.
4Supports queuing, logging, and scheduling of requests.
5Real examples: GUI actions, transactions, Redux actions.
6Each command stores all info needed to execute and undo.