Behavioral

Command Pattern

Encapsulate a request as an object to support undo, queuing, and logging. The backbone of undo/redo systems, macro recorders, job queues, and transactional operations.

Overview ยท Behavioral ยท Singleton ยท Factory Method ยท Abstract Factory ยท Builder ยท Prototype ยท Adapter ยท Bridge ยท Composite ยท Decorator ยท Facade ยท Flyweight ยท Proxy ยท Observer ยท Strategy ยท Command ยท Template Method ยท Chain of Resp. ยท State ยท Mediator ยท Iterator ยท Visitor ยท Memento ยท Interpreter
Category: Behavioral Difficulty: Intermediate Interview: Tier 2 Confused with: Strategy
01
Section One ยท The Problem

Why Command Exists

You are building a text editor. The toolbar has buttons for Bold, Italic, Underline, and Delete. Each button directly calls a method on the Document. Now you need to add undo/redo, macro recording, and keyboard shortcuts that trigger the same operations. The naive approach: every UI element hard-codes the operation logic.

Naive approach — operations hard-wired into UI
// โœ— Each button knows exactly what to do โ€” no undo, no queuing class BoldButton { private Document doc; public void onClick() { doc.applyBold(doc.getSelection()); // โ† hard-coded action // How to undo this? Need to store previous state here? // How to replay this as a macro? Duplicate the logic? } } class DeleteButton { private Document doc; public void onClick() { doc.delete(doc.getSelection()); // โ† different hard-coded action // Same undo problem. Same macro problem. } } // Keyboard shortcut Ctrl+B? Duplicate BoldButton logic. Menu item? Duplicate again.

What goes wrong:

  • No undo/redo — the operation is executed and forgotten; there’s no record of what happened or how to reverse it
  • Duplicated logic — the same operation exists in the button, the keyboard shortcut handler, the menu item, and the macro player
  • Tight coupling — every UI element depends directly on Document’s API; changing the document model means editing every button class
  • No queuing or logging — you can’t serialize operations, schedule them for later, or replay them for debugging
Without Command — UI elements hard-wired to receiver methods
BoldButton ItalicButton DeleteButton Document ✗ No undo โ€” action fires and is forgotten ✗ Ctrl+B duplicates BoldButton logic ✗ Can't queue, log, or replay operations ✗ Every trigger point coupled to Document API

This is the problem Command solves — encapsulate each operation as a self-contained object that knows how to execute() and undo() itself. The invoker (button, shortcut, macro) holds a Command reference and doesn’t know or care what the command does internally.

02
Section Two ยท The Pattern

What Is Command?

Command is a behavioral pattern that encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Instead of a button directly calling document.applyBold(), the button holds a Command object and calls command.execute(). The command object contains all the information needed to perform the action — and to reverse it.

GoF Intent: “Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.”
— Gamma, Helm, Johnson, Vlissides (1994)
Analogy — restaurant order slip: When you order food at a restaurant, you don’t walk into the kitchen and cook it yourself. The waiter writes your order on a slip (the command object). The slip contains everything needed: dish name, modifications, table number. The waiter (invoker) doesn’t know how to cook — they just pass the slip to the kitchen (receiver). The kitchen executes the order. The slip can be queued (busy night), logged (receipt), or cancelled (you change your mind). The waiter doesn’t know or care about the recipe — they only know that every slip has an “execute” (prepare the dish). This is Command: the request is an object that can be stored, passed around, queued, undone, and replayed.

Key insight: Command turns a verb (an action) into a noun (an object). Once an action is an object, you can do anything with it that you can do with any object: store it in a list (history), serialize it (audit log), pass it to another thread (job queue), or call a reverse method on it (undo). This single transformation — action-as-object — unlocks undo/redo, macro recording, transaction rollback, and task scheduling.

Each operation is a class implementing Command with execute() and undo()
The invoker (button, shortcut, scheduler) doesn’t know what the command does — it just calls execute()
Executed commands are pushed onto a history stack
Undo = pop the last command and call undo(). Redo = push it back and call execute() again
Commands are objects — they can be serialized, queued, batched
Macro recording = store a list of commands. Replay = iterate and execute() each. Transaction log = serialize the list.
Multiple trigger points (button, menu, shortcut) share the same Command instance
Zero duplication. Change the operation logic in one place — all triggers reflect it immediately.
03
Section Three ยท Anatomy

Participants & Structure

Participant Role In the Analogy
Command (interface) Declares execute() and optionally undo(). Every concrete command implements this. The invoker depends only on this interface — never on concrete commands. The order slip format — every slip has a “prepare” action regardless of the dish.
Concrete Command Implements execute() by calling specific methods on the Receiver. Stores any state needed for undo() (e.g. previous text, old selection). A specific order slip: “1ร— Margherita, extra basil, table 5.”
Receiver The object that actually performs the work. The command delegates to it. The receiver doesn’t know about commands — it just exposes domain methods. The kitchen — knows how to cook every dish but doesn’t know about order slips.
Invoker Holds a command reference and triggers execute(). Doesn’t know what the command does. May maintain a history stack for undo/redo. The waiter — collects slips and passes them to the kitchen. Doesn’t cook.
Client Creates concrete command objects, wires them to receivers, and assigns them to invokers. The only participant that knows the full picture. The restaurant manager who designs the menu (which dish maps to which recipe).
Command — UML Class Diagram
«interface» Command + execute() : void + undo() : void Invoker - history : Stack<Command> + executeCommand(cmd) + undoLast() command Document - content : String + applyBold(range) + delete(range) BoldCommand - doc : Document - range : Range + execute() + undo() DeleteCommand - doc : Document - backup : String + execute() + undo() MacroCommand - commands : List<Command> + execute() + undo() ■ Blue = interface ■ Green = concrete command ■ Dark = invoker / receiver --▷ dashed = implements --> dashed thin = dependency (uses receiver) Invoker knows only Command interface โ€” never concrete commands
Command vs. Strategy structure:
  • Both have an interface with concrete implementations.
  • The difference is intent: Strategy swaps an algorithm for an interchangeable behaviour.
  • Command encapsulates a request with full context, enabling undo, queuing, and history.
  • A Strategy is stateless (“how to sort”); a Command carries state (“what to undo”).
04
Section Four ยท How It Works

The Pattern In Motion

Scenario: A text editor with undo/redo. The user selects text, clicks Bold, then clicks Delete, then presses Ctrl+Z to undo the delete.

Step 1 — Client wires commands: boldBtn.setCommand(new BoldCommand(doc))
The button (invoker) now holds a Command reference. It doesn’t know it’s a BoldCommand — just that it can call execute() and undo().
Step 2 — User clicks Bold: invoker calls command.execute()
BoldCommand.execute() saves the current text as backup, then calls doc.applyBold(selection). The invoker pushes this command onto the history stack.
Step 3 — User clicks Delete: invoker calls deleteCmd.execute()
DeleteCommand.execute() saves the deleted text as backup, then calls doc.delete(selection). Pushed onto history stack (now 2 items).
Step 4 — User presses Ctrl+Z: invoker calls undoLast()
Invoker pops DeleteCommand from the history stack and calls deleteCmd.undo(). The command restores the deleted text from its backup. Document is back to post-bold state.
Step 5 — User presses Ctrl+Z again
Invoker pops BoldCommand and calls boldCmd.undo(). The command restores the original unbolded text. Document is back to the initial state. History stack is empty.
Command — Execute + Undo Sequence
Invoker BoldCommand DeleteCommand Document execute() applyBold(selection) โ†’ push BoldCmd to history execute() delete(selection) โ†’ push DeleteCmd to history undo() [Ctrl+Z] restore(backup) โ† pop DeleteCmd from history undo() [Ctrl+Z] removeBold(range)
The pattern in pseudocode
// โ”€โ”€ Setup (Client wires everything) โ”€โ”€ Document doc = new Document("Hello World"); EditorInvoker editor = new EditorInvoker(); // โ”€โ”€ User clicks Bold โ”€โ”€ Command bold = new BoldCommand(doc, 0, 5); editor.executeCommand(bold); // Output: Applied bold to "Hello" // โ”€โ”€ User clicks Delete โ”€โ”€ Command del = new DeleteCommand(doc, 6, 11); editor.executeCommand(del); // Output: Deleted "World" // โ”€โ”€ User presses Ctrl+Z โ”€โ”€ editor.undoLast(); // Output: Undo delete โ†’ restored "World" // โ”€โ”€ User presses Ctrl+Z again โ”€โ”€ editor.undoLast(); // Output: Undo bold โ†’ restored original "Hello"
Behavioral patterns need both static UML and dynamic sequence:
  • The UML (S03) shows who participates. The sequence diagram above shows who calls whom in what order.
  • For Command, the key runtime insight is: execute() delegates to the receiver and stores undo state; undo() reverses the action using that stored state.
  • The invoker orchestrates the history stack but never knows the details.
05
Section Five ยท Java Stdlib

You Already Use This

Command is embedded deeply in Java’s concurrency model and GUI framework. Every time you pass a Runnable to a thread pool, you’re using Command — encapsulating an action as an object that can be queued, scheduled, and executed later.

IN JAVA
Example 1 java.lang.Runnable — the simplest Command interface in Java. A single method: run(). You create a Runnable (concrete command), pass it to a Thread or ExecutorService (invoker), and it executes the encapsulated action. The executor doesn’t know what the Runnable does — it just calls run().
Example 2 java.util.concurrent.Callable<V> — a Command that returns a result. call() is the execute() method. Submitted to an ExecutorService (invoker), which queues it and returns a Future<V>. The invoker can schedule, cancel, or retry the command without knowing its internals.
Example 3 javax.swing.Action — a rich Command interface for Swing GUIs. Extends ActionListener with name, icon, enabled state, and actionPerformed(). A single Action object can be shared by a toolbar button, menu item, and keyboard shortcut — exactly what Command prescribes. Change the action’s enabled state, and all three UI elements update.
Example 4 java.util.TimerTask — a scheduled command. Extends Runnable with scheduling metadata. The Timer (invoker) queues TimerTask objects and executes them at specified times. Same pattern: encapsulated action + deferred execution.
Stdlib usage — Runnable as Command
// Runnable = Command interface, ExecutorService = Invoker ExecutorService pool = Executors.newFixedThreadPool(4); // Encapsulate actions as command objects Runnable sendEmail = () -> System.out.println("Sending email..."); Runnable generatePdf = () -> System.out.println("Generating PDF..."); Runnable resizeImage = () -> System.out.println("Resizing image..."); // Invoker queues and executes โ€” doesn't know what each command does pool.submit(sendEmail); pool.submit(generatePdf); pool.submit(resizeImage); pool.shutdown();
Stdlib usage — Swing Action shared across UI elements
// One Action (command) shared by button, menu, and shortcut Action saveAction = new AbstractAction("Save") { public void actionPerformed(ActionEvent e) { document.save(); // receiver } }; // Same command object โ†’ three trigger points, zero duplication toolbar.add(saveAction); // toolbar button fileMenu.add(saveAction); // menu item inputMap.put(ctrlS, "save"); // keyboard shortcut actionMap.put("save", saveAction);
Why Runnable is Command, not Strategy:
  • A Comparator (Strategy) defines how to compare — it’s stateless and interchangeable.
  • A Runnable (Command) encapsulates a specific action to execute later — it’s queued, scheduled, or passed to another thread.
  • The key difference: Commands are created, stored, and potentially undone. Strategies are swapped.
06
Section Six ยท Implementation

Build It Once

Domain: Text Editor Undo/Redo. A TextDocument receiver holds editable content. Commands (BoldCommand, DeleteCommand) encapsulate operations with full undo support. An EditorInvoker maintains command history and provides undo/redo.

Java — Command Pattern Text Editor (core)
// โ”€โ”€ Command interface โ”€โ”€ interface Command { void execute(); void undo(); String description(); } // โ”€โ”€ Invoker with history โ”€โ”€ class EditorInvoker { 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(); // new action invalidates redo } 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); } } }
Why the redo stack clears on new execute:
  • If you undo twice (A โ†’ B โ†’ C becomes A โ†’ B), then execute a new command D, the redo stack (which held C) must be cleared.
  • Otherwise redo would restore C, which was based on state that no longer exists.
  • This is how every real editor works — new actions after undo invalidate the redo branch.
07
Section Seven ยท Watch Out

Common Mistakes

Mistake #1 — Forgetting to store undo state before execute: A DeleteCommand calls doc.delete() but never stores what was deleted. When undo() is called, there’s nothing to restore. Fix: Always capture the “before” state inside execute() before mutating the receiver. For delete: save the deleted text. For bold: save the original range. For insert: you just need the position and length to reverse.
✗ Wrong — no backup stored
// โœ— execute() deletes text but never saves it class DeleteCommand implements Command { public void execute() { doc.delete(start, end); // โ† text is gone forever } public void undo() { // โœ— What do we insert? We didn't save the deleted text! doc.insert(start, ???); } }
✔ Correct — backup before mutate
// โœ“ Save deleted text inside execute(), use it in undo() class DeleteCommand implements Command { private String backup; public void execute() { backup = doc.getText(start, end); // โ† save first doc.delete(start, end); // โ† then mutate } public void undo() { doc.insert(start, backup); // โ† restore from backup } }
Mistake #2 — Making commands stateless (confusing Command with Strategy):
  • If your command doesn’t store any state — no receiver reference, no parameters, no backup — it’s not a Command, it’s a Strategy.
  • A Command must carry the context needed to execute and undo a specific request.
  • A “BoldCommand” without knowing which range to bold and what was there before is useless for undo.
Mistake #3 — Not clearing the redo stack on new commands:
  • User undoes action A, then performs a new action B.
  • If the redo stack still holds A, pressing redo would try to re-execute A on a state it no longer matches — causing corruption.
  • Fix: Always call redoStack.clear() in execute(). Every real editor does this — once you type after undoing, the undone branch is lost.
Mistake #4 — Coupling the invoker to concrete commands:
  • If the invoker imports BoldCommand, DeleteCommand, etc., you’ve lost the decoupling benefit.
  • The invoker should know only the Command interface.
  • The client (setup code) is responsible for creating concrete commands and assigning them to the invoker.
  • Fix: Invoker accepts Command; client does button.setCommand(new BoldCommand(doc, range)).
Mistake #5 — Unbounded history stack:
  • Every executed command stays in memory forever.
  • In a real editor, 10,000 operations means 10,000 command objects — each potentially holding a backup string.
  • Fix: Cap history size (e.g. 100 undos). Use circular buffer or Deque with max capacity. For large state, consider Memento pattern for efficient snapshots.
08
Section Eight ยท Decision Guide

When To Use Command

Use Command When
  • You need undo/redo — each operation must be reversible, and the system must track execution history
  • You want to queue, schedule, or log operations — requests must exist as objects that can be stored and replayed
  • Multiple trigger points (button, menu, shortcut, API) must execute the same operation without duplicating logic
  • You need macro recording — record a sequence of commands and replay them as a batch
  • You want to decouple the object that invokes an operation from the object that performs it — the invoker should not know about the receiver
  • You need transactional behaviour — execute a batch of commands, and roll them all back if one fails
Avoid Command When
  • The operation is simple and one-shot — no undo, no queuing, no history needed; a direct method call is simpler
  • You only need to swap algorithms at runtime without tracking history — use Strategy instead
  • The action has no meaningful undo — sending an email or charging a credit card can’t be reversed by calling undo(); consider Saga or compensating transactions instead
  • There is only one invoker and one receiver with a stable API — the indirection of Command adds complexity without benefit
Command vs. Confused Patterns
Pattern Encapsulates Carries State? When to pick it
Command ← this A request (action + receiver + params) Yes — backup for undo, params for execute Undo/redo, queuing, logging, macros
Strategy An algorithm (how to do something) No — stateless, interchangeable Swap behaviour at runtime without history
Observer A notification (one-to-many broadcast) No — fire and forget Multiple listeners react to state change
Memento An object’s internal state snapshot Yes — full state copy Undo by restoring full state (not action-based)
Chain of Responsibility A request passed along a chain No — request handled by one handler Request needs to find the right handler
Decision Flowchart
Need to encapsulate an operation? No Direct call Yes Need undo, history, or queuing? No Strategy (swap algorithm only) Yes Undo by reversing action (not full state)? No Memento (full state snapshot) Yes Command
09
Section Nine ยท Practice

Problems To Solve

Command problems test whether you can encapsulate operations as objects, implement reversible execution, manage history stacks, and handle edge cases like macro batching and transactional rollback.

Difficulty Problem Key Insight
Easy Remote Control
Build a universal remote control with 7 slots. Each slot can be assigned a Command (e.g. turn on light, start fan, open garage). The remote has an “undo” button that reverses the last action. Commands can be reassigned at runtime.
Tests the basic Command structure: interface with execute()/undo(), invoker with slots, and receiver objects. The key part: the remote knows nothing about lights or fans — it only calls command.execute(). Undo stores the last executed command and calls undo() on it.
Medium Database Transaction with Rollback
Build a TransactionManager that executes a batch of Commands (INSERT, UPDATE, DELETE). If any command throws an exception, the entire batch is rolled back by calling undo() on all previously executed commands in reverse order.
Tests transactional Command usage. The TransactionManager (invoker) executes commands sequentially, storing each in a “done” list. On failure, it iterates the done list in reverse calling undo(). Each command stores enough state to reverse itself (e.g. DELETE saves the row, INSERT saves the generated ID for removal).
Medium Macro Recorder
Build a MacroCommand that records a sequence of user actions and replays them. The user presses “Record”, performs actions (bold, insert, delete), presses “Stop”, then can “Play” the macro on any document. The macro itself supports undo() (undoes all actions in reverse).
Tests composite Command (a command that holds a list of commands). MacroCommand.execute() iterates the list and calls execute() on each. MacroCommand.undo() iterates in reverse and calls undo(). The macro is itself a Command — it can be stored in the history stack like any other command.
Hard Collaborative Editor with Operational Transform
Two users edit the same document simultaneously. Each keystroke is a Command sent to a server. The server must transform and reorder concurrent commands so that both users converge to the same document state. Implement transform(cmd1, cmd2) for insert-insert conflicts.
Tests advanced Command usage in distributed systems. Each command carries position + content. When two inserts arrive concurrently, the server transforms the second command’s position based on the first command’s effect. This is the foundation of Google Docs — commands (operations) as first-class objects enable transformation, reordering, and conflict resolution.
Interview Tip:
  • When asked about Command, the interviewer wants to see: (1) a Command interface with execute() and undo(); (2) concrete commands that store receiver + params + undo state; (3) an invoker that maintains a history stack; (4) clear separation — invoker depends only on the Command interface.
  • Stand-out answers mention: the difference from Strategy (stateless algorithm vs. stateful request), how Runnable/Callable are Commands in the JDK, how to handle the redo stack (clear on new execute), and how Command + Memento compose for complex undo (Command for action-based undo, Memento for full-state snapshots when actions are too complex to reverse incrementally).