Skip to main content
Design Patterns – Complete Beginner to Advanced Guide
CHAPTER 15 Intermediate

Command Pattern

Updated: May 16, 2026
35 min read

# CHAPTER 15

Command Pattern

1. Introduction

Imagine building a text editor. You have a "Save" button in the top menu. You also have a "Save" shortcut (Ctrl+S). You also have a "Save" option in the right-click context menu. If you write the complex logic for saving the file directly inside the UI click-handler code for all three of those triggers, you have duplicated heavy business logic three times. Worse, how do you implement an "Undo" feature if the actions are just floating functions? The Command Pattern solves this. It turns a request or an action into a standalone, physical object. In this chapter, we will master the Command Pattern, learning how to decouple UI from business logic, build robust asynchronous task queues, and engineer complex Undo/Redo systems.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Define the intent of the Command Pattern.
  • Understand how encapsulating an action into an object creates architectural flexibility.
  • Map the roles of the Sender (Invoker), the Command, and the Receiver.
  • Architect an "Undo/Redo" history stack.
  • Implement delayed execution for Task Queues.

3. The Core Concept

The Command pattern encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
  • The Decoupling: The UI Button (The Sender) should not know *how* to save a file to a database. It should only know how to execute a Command object.
  • The Objectification of Action: By turning an action into an object, you can store it in an array. If you have an array of "Executed Commands," you have a history log. If you loop backward through that array calling undo(), you have just built Ctrl+Z.

4. The Four Actors

The Command pattern relies on a very specific, slightly complex structure.
  1. 1. The Receiver: The underlying business logic that actually does the work (e.g., The Database or the TextDocument).
  1. 2. The Command Interface: A simple contract, usually containing just execute() and undo().
  1. 3. The Concrete Command: The object that binds a specific action to a specific Receiver (e.g., SaveFileCommand).
  1. 4. The Invoker (Sender): The UI button or shortcut manager. It holds the Command object and calls execute() when clicked.

5. Task Queues and Asynchronous Execution

Because a Command is a physical object containing all the data it needs to execute, you don't have to execute it immediately. You can serialize the Command object, push it into a Redis queue, and have a background worker deserialize it and execute it 10 minutes later. The Command pattern is the foundation of all job queue architectures.

6. UML Diagram

*Command Structure*
text
12345678
[ Invoker (Button) ] ------------> [ <<Interface>> Command ]
- command: Command                       + execute()
+ click() { command->execute() }         + undo()
                                              ^
                                              | (Implements)
                                     [ ConcreteCommand ] -------> [ Receiver ]
                                       - receiver: Receiver         + action()
                                       + execute() { r->action() }

7. Code Example (PHP)

Let's build a simple Smart Home Remote with an Undo feature.
php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
<?php
// --- 1. The Receiver (The actual device doing the work) ---
class Light {
    public function turnOn() { echo "Light is ON.\n"; }
    public function turnOff() { echo "Light is OFF.\n"; }
}

// --- 2. The Command Interface ---
interface Command {
    public function execute();
    public function undo();
}

// --- 3. Concrete Commands ---
class LightOnCommand implements Command {
    private $light;
    public function __construct(Light $light) { $this->light = $light; }
    
    public function execute() { $this->light->turnOn(); }
    public function undo() { $this->light->turnOff(); } // The exact opposite
}

class LightOffCommand implements Command {
    private $light;
    public function __construct(Light $light) { $this->light = $light; }
    
    public function execute() { $this->light->turnOff(); }
    public function undo() { $this->light->turnOn(); }
}

// --- 4. The Invoker (The Remote Control) ---
class RemoteControl {
    private $commandHistory = [];

    // The remote doesn't know it's turning on a light. 
    // It just executes whatever Command object it's given.
    public function pressButton(Command $cmd) {
        $cmd->execute();
        // Save the command to history for undoing
        array_push($this->commandHistory, $cmd); 
    }

    public function pressUndo() {
        if (!empty($this->commandHistory)) {
            $lastCmd = array_pop($this->commandHistory);
            echo "Undoing last action... ";
            $lastCmd->undo();
        } else {
            echo "Nothing to undo.\n";
        }
    }
}

// --- 5. Client Code ---
$livingRoomLight = new Light();
$turnOn = new LightOnCommand($livingRoomLight);
$turnOff = new LightOffCommand($livingRoomLight);

$remote = new RemoteControl();

// 1. Press ON
$remote->pressButton($turnOn);  // Output: Light is ON.

// 2. Press OFF
$remote->pressButton($turnOff); // Output: Light is OFF.

// 3. User makes a mistake, hits UNDO
$remote->pressUndo();           // Output: Undoing last action... Light is ON.
?>

8. Best Practices

  • Mementos for Complex Undo: In the example above, undo() was easy because the opposite of "On" is "Off." But what if the command is "Change Font Color to Red"? How does the Command know what the *previous* color was? *Best Practice:* Before a Concrete Command calls execute(), it should save a snapshot (a Memento) of the Receiver's current state inside its own private variables. Then, undo() simply restores that saved snapshot.

9. Common Mistakes

  • Putting Logic in the Command: A Command should act as a dumb relay. It should *not* contain heavy business logic. If SaveToDatabaseCommand contains 50 lines of SQL queries, it is violating SRP. The Command should simply call $database->save(). The heavy logic belongs in the Receiver.

10. Mini Project: Build a Macro Recorder

  1. 1. Create multiple Commands: CopyCommand, PasteCommand, DeleteCommand.
  1. 2. Create a MacroCommand class that implements Command. Its constructor accepts an array of other Commands.
  1. 3. In MacroCommand->execute(), loop through the array and call execute() on every child command. You have just built a system to record and playback complex sequences of user actions!

11. Practice Exercises

  1. 1. Define the architectural role of the "Invoker" versus the "Receiver" in the Command Pattern. Why is decoupling them critical for GUI development?
  1. 2. Explain exactly how turning an action (like a button click) into a physical Object enables the creation of an "Undo/Redo" history stack.

12. MCQs with Answers

Question 1

An architect needs to implement a robust "Ctrl+Z" (Undo) feature for a complex graphic design application. Which behavioral design pattern is universally recognized as the standard solution for capturing and reversing user actions?

Question 2

In the Command Pattern architecture, which object is responsible for actually performing the heavy business logic (e.g., executing the SQL query to save the file)?

13. Interview Questions

  • Q: Explain how the Command pattern facilitates the creation of asynchronous Job Queues (like AWS SQS or Redis queues). How does "objectifying" a request allow for delayed execution?
  • Q: Walk me through the implementation of a complex undo() method. If a user deletes a block of text, how does the DeleteTextCommand object know how to restore the exact text that was deleted?
  • Q: Compare the Command Pattern with the Strategy Pattern. Both involve encapsulating behavior into objects and executing them via an interface. How do their structural intents differ?

14. FAQs

Q: Doesn't this create way too many tiny classes? A: Yes. A large app might have hundreds of Command classes (SaveCommand, CopyCommand, CutCommand). While it increases file count, it massively decreases complexity. Each class has one tiny responsibility and is perfectly testable in isolation.

15. Summary

In Chapter 15, we materialized actions into tangible entities. By deploying the Command Pattern, we extracted the heavy business logic out of our fragile UI controllers and encapsulated it into standalone, actionable objects. This objectification unlocked immense architectural power: the ability to decouple Senders from Receivers, the capability to serialize tasks for delayed background execution, and the mechanical foundation required to engineer robust, traversable Undo/Redo history stacks. The Command pattern is the bridge between human interaction and backend execution.

16. Next Chapter Recommendation

Our objects can queue and undo commands. But what if an object needs to radically change its entire behavior based on its internal status? Proceed to Chapter 16: State and Chain of Responsibility Patterns.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·