Home/Learn/Java A–Z/Command Pattern

Command Pattern

Intermediate
Design Patterns

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

Overview

The Command pattern encapsulates a request — with all its parameters and context — as a standalone object. This enables: parameterising clients with different requests, queuing or logging requests, implementing undo/redo, and transactional behaviour. The four roles are: Command (interface), ConcreteCommand (implements the action), Invoker (triggers the command), and Receiver (the object that does the actual work). Java's Runnable and Callable are the simplest Commands in the JDK.

Command Pattern Implementation

Define a Command interface with execute(). ConcreteCommands hold a reference to the Receiver and implement execute() by calling receiver methods. The Invoker stores and triggers commands without knowing their type.

This decouples the object that invokes the operation from the object that knows how to perform it.

TextEditorCommand.java
// Command interface
public interface Command {
    void execute();
    void undo();  // optional — enables undo/redo
}

// Receiver — the object that actually does the work
public class TextEditor {
    private final StringBuilder text = new StringBuilder();
    public void insertText(String s) { text.append(s); }
    public void deleteText(int len)  { text.delete(text.length() - len, text.length()); }
    public String getText()          { return text.toString(); }
}

// Concrete Command
public class InsertCommand implements Command {
    private final TextEditor editor;
    private final String text;

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

    @Override public void execute() { editor.insertText(text); }
    @Override public void undo()    { editor.deleteText(text.length()); }
}

// Invoker — triggers commands, maintains history for undo
public class CommandHistory {
    private final Deque<Command> history = new ArrayDeque<>();

    public void execute(Command cmd) {
        cmd.execute();
        history.push(cmd);
    }

    public void undo() {
        if (!history.isEmpty()) history.pop().undo();
    }
}

Command Queue and Macro Commands

Commands can be queued for deferred execution, serialised for persistence, or composed into macro commands (a command that runs other commands in sequence).

This is the foundation of event sourcing: every state change is a Command stored in a log — replaying the log reconstructs the state.

CommandQueue.java
// Command queue — deferred execution
BlockingQueue<Command> commandQueue = new LinkedBlockingQueue<>();

// Producer — enqueue commands
commandQueue.put(new InsertCommand(editor, "Hello "));
commandQueue.put(new InsertCommand(editor, "World"));

// Consumer — execute in order (could be a different thread)
Thread worker = new Thread(() -> {
    while (true) {
        try {
            commandQueue.take().execute();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            break;
        }
    }
});

// Macro Command — compose multiple commands
public class MacroCommand implements Command {
    private final List<Command> commands;

    public MacroCommand(Command... cmds) {
        this.commands = List.of(cmds);
    }

    @Override public void execute() {
        commands.forEach(Command::execute);
    }

    @Override public void undo() {
        // Undo in reverse order
        List<Command> reversed = new ArrayList<>(commands);
        Collections.reverse(reversed);
        reversed.forEach(Command::undo);
    }
}

Command Pattern in Java (Runnable / Functional)

In modern Java, simple commands with no undo are just Runnable or Supplier<T> — lambdas replace concrete Command classes. This is how ExecutorService, CompletableFuture, and event systems work.

Use the full Command pattern (with undo and history) only when you need those capabilities. For fire-and-forget tasks, Runnable/lambda is simpler and equally effective.

LambdaCommand.java
// Runnable IS a Command (no undo, no return value)
Runnable sendEmail = () -> emailService.send(user, "Welcome!");
Runnable logEvent  = () -> auditLog.record("USER_CREATED", user.id());

// Execute commands — decoupled from how they're created
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(sendEmail);
executor.submit(logEvent);

// Button click handler — Command in a GUI
public class Button {
    private Runnable onClick;
    public void setOnClick(Runnable cmd) { this.onClick = cmd; }
    public void click() { if (onClick != null) onClick.run(); }
}

Button saveBtn = new Button();
saveBtn.setOnClick(() -> documentService.save(currentDoc)); // lambda command
saveBtn.setOnClick(() -> {
    documentService.save(currentDoc);
    statusBar.showMessage("Saved!");
});

// With return value — Callable / Supplier
Callable<byte[]> compress = () -> compressor.compress(data);
Future<byte[]>   result   = executor.submit(compress);

Key Points to Remember

  • Command encapsulates a request as an object — decouples invoker from receiver.
  • Storing commands enables undo/redo, queuing, logging, and event sourcing.
  • MacroCommand composes multiple commands; undo reverses them in reverse order.
  • Runnable and Callable are the JDK's built-in Commands for fire-and-forget tasks.
  • Use full Command pattern when you need history/undo; use lambda for simple cases.

Practice Command Pattern in the Playground

Run and modify code directly in your browser - no setup needed.

Interview Questions

Sign in to ask Aria
1

What four roles does the Command pattern define?

MediumAmazon
2

How does Command pattern enable undo/redo functionality?

MediumGoogle
3

How does Runnable relate to the Command pattern?

EasyOracle
4

How would you implement a command queue for async task execution?

MediumMicrosoft
5

What is the connection between Command pattern and event sourcing?

HardNetflix

Ask Aria about Command Pattern

Your personal AI tutor — ask anything about this concept