Home/Learn/Low Level Design/Command Pattern

Command Pattern

Intermediate
Behavioral Patterns

Encapsulates a request as an object, enabling parameterization, queuing, logging, and undo/redo of operations.

Overview

The Command pattern turns a method call into a standalone object. The Command object captures the action, its arguments, and the receiver. The Invoker stores and executes commands without knowing what they do. This enables: queuing (job queue, task scheduler), logging (audit trail of executed commands), and undo/redo (maintaining a history stack of reversible commands). The Command object is the unit of work — it is serializable, transmittable, and replayable. Used in GUI frameworks (menu actions, button clicks), transactional job systems, and macro recording.

Command with Undo/Redo

Each Command has execute() and undo() methods. The Invoker maintains two stacks — history (for undo) and undone (for redo). Executing a command pushes to history; undoing pushes to undone.

Java — Command with Undo/Redo (Text Editor)
import java.util.ArrayDeque;
import java.util.Deque;

// Command interface
public interface Command {
    void execute();
    void undo();
    String description();
}

// Receiver
public class TextEditor {
    private final StringBuilder text = new StringBuilder();

    public void insertText(int position, String s) {
        text.insert(position, s);
    }

    public void deleteText(int position, int length) {
        text.delete(position, position + length);
    }

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

// Concrete Command — insert
public class InsertCommand implements Command {
    private final TextEditor editor;
    private final int position;
    private final String text;

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

    @Override public void execute() { editor.insertText(position, text); }
    @Override public void undo()    { editor.deleteText(position, text.length()); }
    @Override public String description() { return "Insert '" + text + "' at " + position; }
}

// Concrete Command — delete
public class DeleteCommand implements Command {
    private final TextEditor editor;
    private final int position;
    private final int length;
    private String deleted; // saved for undo

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

    @Override
    public void execute() {
        deleted = editor.getText().substring(position, position + length);
        editor.deleteText(position, length);
    }

    @Override public void undo() { editor.insertText(position, deleted); }
    @Override public String description() { return "Delete " + length + " chars at " + position; }
}

// Invoker — manages history
public class CommandInvoker {
    private final Deque<Command> history = new ArrayDeque<>();
    private final Deque<Command> undone  = new ArrayDeque<>();

    public void execute(Command cmd) {
        cmd.execute();
        history.push(cmd);
        undone.clear(); // new command clears redo stack
        System.out.println("Executed: " + cmd.description());
    }

    public void undo() {
        if (history.isEmpty()) { System.out.println("Nothing to undo"); return; }
        Command cmd = history.pop();
        cmd.undo();
        undone.push(cmd);
        System.out.println("Undone: " + cmd.description());
    }

    public void redo() {
        if (undone.isEmpty()) { System.out.println("Nothing to redo"); return; }
        Command cmd = undone.pop();
        cmd.execute();
        history.push(cmd);
        System.out.println("Redone: " + cmd.description());
    }
}

// Usage
TextEditor editor   = new TextEditor();
CommandInvoker ctrl = new CommandInvoker();

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

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

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

Key Points to Remember

  • 1Command encapsulates a request as an object — enables queuing, logging, and undo/redo.
  • 2execute() performs the action; undo() reverses it. The deleted/old state must be saved in execute().
  • 3Invoker knows nothing about the command receiver — it only calls execute()/undo().
  • 4Command pattern is used in Java Swing (AbstractAction), Spring Batch (Job/Step), and transactional outbox.
  • 5A MacroCommand is a Composite of Commands — execute() calls execute() on each child.

Interview Questions

Sign in to ask Aria
1

How would you implement undo/redo in a drawing application using Command pattern?

MediumAdobe
2

What is the difference between Command and Strategy patterns?

MediumAmazon
3

How would you persist a command queue to survive application restarts?

HardGoogle
4

What is a MacroCommand and how does it relate to the Composite pattern?

MediumMicrosoft

Ask Aria about Command Pattern

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…