The Command pattern encapsulates a request as an object, allowing you to parameterize clients, queue operations, and support undo.

Problem

You need to decouple the object that invokes an operation from the object that performs it, and optionally support undo/redo.

Solution

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

class Light {
    void turnOn() { System.out.println("Light ON"); }
    void turnOff() { System.out.println("Light OFF"); }
}

class LightOnCommand implements Command {
    private final Light light;
    LightOnCommand(Light light) { this.light = light; }

    public void execute() { light.turnOn(); }
    public void undo() { light.turnOff(); }
}

class RemoteControl {
    private final List<Command> history = new ArrayList<>();
    private int current = -1;

    void pressButton(Command command) {
        command.execute();
        history.subList(current + 1, history.size()).clear();
        history.add(command);
        current++;
    }

    void undo() {
        if (current >= 0) {
            history.get(current).undo();
            current--;
        }
    }
}

// Usage
RemoteControl remote = new RemoteControl();
Light light = new Light();
remote.pressButton(new LightOnCommand(light));  // Light ON
remote.undo();                                    // Light OFF
  

Command with Lambda

Simple commands can be lambdas (without undo):

  Map<String, Runnable> commands = Map.of(
    "start", () -> System.out.println("Starting..."),
    "stop",  () -> System.out.println("Stopping...")
);
commands.get("start").run();
  

Java Examples

  // Runnable is a command interface
executor.submit(() -> System.out.println("Task executed"));

// Swing Action
Action saveAction = new AbstractAction("Save") {
    public void actionPerformed(ActionEvent e) { saveFile(); }
};

// Spring @Async methods
@Async
public CompletableFuture<Void> sendEmail(EmailCommand command) {
    mailService.send(command);
    return CompletableFuture.completedFuture(null);
}
  

Macro Commands

Combine multiple commands into one:

  class MacroCommand implements Command {
    private final List<Command> commands;

    MacroCommand(List<Command> commands) { this.commands = commands; }

    public void execute() { commands.forEach(Command::execute); }
    public void undo() {
        for (int i = commands.size() - 1; i >= 0; i--) {
            commands.get(i).undo();
        }
    }
}
  

Command Queue

  class CommandQueue {
    private final Queue<Command> queue = new LinkedList<>();

    void add(Command command) { queue.offer(command); }

    void processAll() {
        while (!queue.isEmpty()) {
            queue.poll().execute();
        }
    }
}
  

When to Use

  • Decouple invoker from receiver
  • Support undo/redo operations
  • Queue, log, or schedule operations
  • Build macro commands from simple ones

Best Practices

  • Keep commands stateless when possible — store state in the receiver
  • Implement undo() for all reversible commands
  • Use command queues for transaction-like batch processing
  • Consider whether event-driven architecture (Spring events) replaces manual command objects