Design Pattern

Command Pattern

Clean Java-only production-ready implementation.


Encapsulate a request as an object. This lets you parameterize clients with different requests, queue or log requests, and support undoable operations.

// ─── EXAMPLE 1 ──────────────────────────────────────────────────────────────
// WHAT WE ARE IMPLEMENTING:
// A text editing canvas providing complete copy, paste, and multi-level
// action undo/redo tracking.
//
// WHERE THE COMMAND FITS IN:
// Command is the Command interface declaring execute(). CopyCommand is a
// Concrete Command. Editor represents the Receiver. CommandInvoker triggers
// actions.
// ────────────────────────────────────────────────────────────────────────────
import java.util.ArrayDeque;
import java.util.Deque;

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

// --- Receiver ---
class TextEditor {
    private final StringBuilder content = new StringBuilder();

    public void append(String text) { content.append(text); }
    public void deleteLast(int length) {
        int start = Math.max(0, content.length() - length);
        content.delete(start, content.length());
    }
    public String getContent() { return content.toString(); }
}

// --- Concrete commands ---
class AppendCommand implements Command {
    private final TextEditor editor;
    private final String text;

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

    public void execute() {
        editor.append(text);
    }

    public void undo() {
        editor.deleteLast(text.length());
    }
}

// --- Invoker (with undo stack) ---
class CommandHistory {
    private final Deque<Command> history = new ArrayDeque<>();
    private final Deque<Command> redoStack = new ArrayDeque<>();

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

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

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

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

        history.execute(new AppendCommand(editor, "Hello "));
        history.execute(new AppendCommand(editor, "World!"));

        System.out.println("After: " + editor.getContent());  // Hello World!

        history.undo();
        System.out.println("After undo: " + editor.getContent());  // Hello

        history.redo();
        System.out.println("After redo: " + editor.getContent());  // Hello World!
    }
}