ReviseAlgo Logo

Behavioral Patterns

Command

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

Last Updated: June 26, 2026 24 min read

The Command Pattern is a behavioral design pattern that turns a request or action into a stand-alone object containing all details about the execution. This encapsulation decouples the object triggering the action (the Invoker) from the object containing the actual execution steps (the Receiver), enabling request queuing, remote execution, and comprehensive undo/redo histories.

1. Learning Objectives

  • Deconstruct tightly coupled event triggers and encapsulate them into polymorphic commands.
  • Construct stack-based undo/redo command history mechanisms.
  • Evaluate heap footprint costs of retaining execution states in undo histories.
  • Compare the roles of Command, Strategy, and Memento patterns in managing operations.
  • Implement transaction-safe macros (composite commands) in Java, Python, and modern C++.

2. Problem & Naive Solution

Suppose you are building a text editor desktop application. The editor features a top menu bar with buttons for executing operations: Save, Write Text, and Delete Text.

The Naive Solution

A developer might implement button classes that inherit from a base button, coupling them directly to the TextEditor document receiver class:

This direct-access design introduces significant design flaws:

  • Extreme Subclass Bloat: If you add 50 menu buttons, you must write 50 distinct button subclasses. If you need to trigger "Save" via keyboard shortcut (e.g. Ctrl+S) or context menu, you must duplicate that execution logic.
  • No Support for Undo/Redo: Since method calls on TextEditor are executed inline, there is no centralized structure tracking the history of edits, blocking undo support.
  • High Coupling: The GUI components depend directly on concrete document classes, preventing the reuse of buttons in other application views.

3. Issues

Direct API execution limits flexibility. Without encapsulating requests, you cannot queue commands for delayed execution, log user actions for crash recovery, or coordinate multi-operation macros polymorphically.

4. Pattern Introduction & UML

The Command Pattern addresses these issues by wrapping all details of a request inside a Command class. The button (Invoker) only references a generic Command interface. The concrete command class (e.g., WriteCommand) holds a reference to the TextEditor (Receiver) and the text parameters. When clicked, the button calls command.execute(). To undo the edit, it calls command.undo().

UML: Text Editor Commands

5. Participants

  • Command (Command): The interface declaring the execution (execute()) and rollback (undo()) methods.
  • Concrete Command (WriteCommand): Binds the action parameters to the receiver, implementing the execution and backup step logic.
  • Receiver (TextEditor): The object performing the actual work (modifying text buffers, writing to files).
  • Invoker (Button, ShortcutPanel): Triggers command executions, referencing only the Command interface.

6. Theory (Centralized History & Comparisons)

The Command pattern simplifies managing application history:

  • Undo Stack Mechanics: The application maintains a stack of executed commands. When the user executes a command, it is pushed onto the undo stack. When the user triggers undo, the top command is popped and its undo() method is executed, reverting the receiver's state.
  • Comparison: - Command: Encapsulates a request as an object, including parameters and rollback steps. - Strategy: Defines interchangeable algorithms for a task (focuses on *how* to do something, not tracking *what* was done). - Memento: Captures and externalizes an object's internal state snapshot, often used by commands to restore state without exposing private fields.

7. Syntax Explanation

Key implementation details in different languages:

  • Java: Commands are modeled as classes implementing a Command interface. Stack history is managed using collections like Deque (e.g. ArrayDeque).
  • Python: Leverages callables (objects overriding __call__) to run commands dynamically, or passes lambda expressions as callbacks.
  • C++: Uses std::unique_ptr queues to manage dynamic polymorphism safely, ensuring proper destruction of command instances when history is cleared.

8. Step-by-Step Implementation

  1. Step 1: Create the Command interface with execute() and undo() methods.
  2. Step 2: Implement the Receiver class representing the business object (e.g. TextEditor).
  3. Step 3: Create concrete command classes that accept the receiver and parameters in their constructor.
  4. Step 4: Inside the concrete commands, write the execute() steps and corresponding undo() rollback steps.
  5. Step 5: Create the Invoker class that maintains the undo/redo stacks, executing commands and pushing them onto the stack.

9. Complete Code (Mini Project)

10. Code Walkthrough

Let's trace the execution logic:

  • Decoupled Invoker: The EditorController only calls the generic execute() and undo() methods, remaining completely decoupled from text buffer manipulations.
  • Encapsulated Rollback State: The DeleteTextCommand dynamically stores the deleted characters in its private state (backedUpText) during execution, allowing it to restore the text if undone.
  • Dynamic History Tracking: The EditorController manages execution history using Deques. Each new action pushes the command onto the undo stack and clears the redo history.

11. Execution Flow

  1. Command Setup: Client instantiates a command object (e.g. WriteTextCommand), binding the receiver and parameters.
  2. Execution & Push: The controller invokes command.execute(), which delegates the task to the receiver, and pushes the command onto the undo stack.
  3. Undo Trigger: The client triggers undo. The controller pops the command from the undo stack and invokes command.undo().
  4. State Restoration: The command uses its cached parameters and state backups to revert the receiver to its previous state.

12. Internal Working (Memory Management of History Stacks)

Retaining command histories in memory requires careful lifecycle management:

  • Stateful Command Retention: Since commands in the history stack hold state backups (like the deleted text buffer strings), they consume heap space. A deep undo stack (e.g., thousands of operations) can lead to significant memory usage.
  • Preventing Memory Leaks: To avoid running out of memory, enforce a maximum size limit on the history stack (e.g., max 100 entries), discarding oldest entries when the limit is exceeded.

13. Complexity Analysis

  • Time Complexity: $O(1)$ constant time overhead to execute, push, pop, or undo commands.
  • Space Complexity: $O(N \times S)$ where $N$ is the maximum history stack size limit and $S$ is the memory size of each command's state backups.

14. Best Practices

  • Limit History Size: Enforce a strict maximum capacity on your undo stacks to protect application memory from growing indefinitely.
  • Enforce Receiver Delegation: Do not write business logic inside the command class. The command should only act as a router delegating tasks to the receiver.

15. Common Mistakes

  • Infinite Undo History: Retaining a complete history of all user actions without an eviction policy, causing memory exhaustion.
  • Writing Core Logic in Commands: Implementing text manipulation algorithms inside WriteTextCommand instead of delegating the work to the TextEditor receiver.

16. Framework Usage

  • Runnable & Callable Interfaces: Java's concurrency framework uses standard Runnable and Callable interfaces to encapsulate tasks, allowing them to be queued and executed by threads.
  • Redux / Flux State Actions: Modern front-end frameworks encapsulate state modifications inside action objects, dispatching them to stores to update views in an undo-safe manner.

17. Interview Discussion

Q: How does the Command pattern compare with the Memento pattern for implementing undo/redo operations?
Answer: - Command: Restores state by executing compensating reverse logic (e.g. deleting written text), consuming less memory but requiring custom rollback logic for every operation. - Memento: Restores state by replacing the receiver's state with a saved snapshot, consuming more memory but simplifying rollback logic for complex states.
Q: How do you implement a Macro Command (a composite command)?
Answer: Use the Composite Pattern. Create a MacroCommand class containing a list of sub-commands. Calling execute() on the macro loops over and executes each sub-command in sequence.
Q: What is the main benefit of decoupling the Invoker from the Receiver?
Answer: Decoupling allows you to write reusable invokers (like generic buttons or menus) that can execute any action simply by injecting different concrete command implementations.

18. Practice Exercises

  • Easy: Write a Python program containing a custom Calculator receiver supporting Add and Subtract commands with undo capabilities.
  • Medium: Design a HomeAutomationSystem where a RemoteControl invoker maps buttons to commands controlling Stereo and AirConditioner receivers.
  • Hard: Build a file system transaction controller supporting CreateFile and DeleteFile operations with automatic rollbacks if a step in a multi-file macro fails.

19. Challenge Problem

Design a Database Transaction Execution Engine. The engine queues database mutation operations (Insert, Update, Delete query commands). If any single command fails to execute, the controller must rollback all previously executed commands in reverse sequence to preserve database transaction integrity. Write this engine in Java, Python, or C++ and test it by executing a composite command that triggers rollbacks on a mock database node.

20. Summary & Cheat Sheet

  • Command encapsulates requests inside dedicated objects, decoupling invokers from receivers.
  • Enables history tracking and rollback support via undo/redo stacks.
  • Limit the capacity of history stacks to prevent heap memory exhaustion.
  • Delegate core execution algorithms to receiver classes, keeping commands thin.

21. Quiz

1. What is the primary purpose of the Command design pattern?

A) To adapt incompatible interfaces
B) To encapsulate a request as an object, decoupling the invoker from the receiver (Correct)
C) To manage class instantiation pools

2. Which participant in the Command pattern performs the actual business work?

A) Invoker
B) Command
C) Receiver (Correct)

3. How does the Command pattern support rollback (undo) operations?

A) By restarting the application thread
B) By storing execution parameters and state backups in command history stacks (Correct)
C) By compiling classes to read-only formats

4. What is the main benefit of decoupling GUI buttons from document receivers?

A) It speeds up compilation times
B) It allows buttons to execute any command dynamically without hardcoding actions (Correct)
C) It avoids virtual dispatch overhead

5. Which of the following is a classic example of the Command pattern in Java concurrency?

A) java.lang.Runnable (Correct)
B) java.util.ArrayList
C) java.lang.ClassLoader

6. What memory leak risk is introduced by storing history commands indefinitely?

A) Stack Overflow
B) Heap Memory Exhaustion (OOM) due to cached command states (Correct)
C) Thread resource leaks

7. How does the Command pattern differ from the Strategy pattern?

A) Strategy changes structural layouts; Command does not
B) Command encapsulates actions and history states; Strategy encapsulates algorithms to select *how* to do tasks (Correct)
C) Command is limited to single-threaded executions

8. What pattern is typically combined with Command to execute multi-command macros?

A) Composite Pattern (Correct)
B) Proxy Pattern
C) Adapter Pattern

9. In C++, why is std::unique_ptr used to store commands inside history stacks?

A) To speed up string allocations
B) To manage command ownership and automate memory releases when items are popped (Correct)
C) To enforce thread synchronization

10. Should core database parsing or formatting logic be written inside Command classes?

A) Yes, to maintain self-contained commands
B) No, it should be delegated to the Receiver to keep command classes thin (Correct)
C) Only in single-threaded desktop applications

22. Next Lesson Preview

In the next lesson, we will explore the State Pattern. We will learn how to allow an object to alter its behavior when its internal state changes, making it look as if the object changed its class!