Command Design Pattern
The Command Design Pattern is a behavioral pattern that turns a request into a stand-alone object. This object contains all the information…
Command Design Pattern
The Command Design Pattern is a behavioral pattern that turns a request into a stand-alone object. This object contains all the information about the request: the method being called, the object it belongs to, and the parameters.
Think of it like a restaurant: The waiter (Invoker) takes your order (Command) and puts it on the counter. The chef (Receiver) then picks up the order and cooks it. The waiter doesn’t need to know how to cook; they just need to trigger the “order” object.
Core Components
The pattern relies on four main pillars to decouple the sender from the receiver:
- Command Interface: Usually a simple interface with an
execute()method. - Concrete Command: Implements the interface and links a specific action to a Receiver.
- Receiver: The object that actually knows how to perform the business logic (the “Chef”).
- Invoker: The object that triggers the command (the “Waiter” or a “Button”).
- Client: Creates the Concrete Command and assigns it to the Receiver.
Why Use the Command Pattern?
The primary goal is decoupling. It separates the object that invokes the operation (the “Invoker”) from the object that knows how to perform it (the “Receiver”).
Problems It Solves
- Request Parameterization: You can pass different commands to a single button or menu item without the button needing to know what each command does.
- Queueing and Scheduling: Since commands are objects, you can put them in a list or a queue to be executed later (like a thread pool or a job scheduler).
- Undo/Redo Functionality: By storing a history of command objects, you can iterate backward through them to reverse operations.
- Macro Commands: You can combine multiple commands into a single “composite” command (e.g., a “Start System” command that turns on lights, starts the PC, and opens the browser).
Real Use Cases
Here are the most common real-world applications:
1. GUI Buttons and Menu Items
This is the “classic” use case. In frameworks like Java Swing or JavaFX, an Action or EventHandler is essentially a command.
- The Problem: A
Buttonclass shouldn't know what happens when it’s clicked (e.g., "Save," "Print," or "Exit"). - The Solution: The button holds a reference to a
Commandobject. When clicked, it just callscommand.execute(). This allows the same Button class to be reused for thousands of different actions.
2. Thread Pools and Job Queues
In high-scale backends, you often don’t want to execute a heavy task (like generating a PDF or sending 10,000 emails) immediately upon a user request.
- The Mechanism: You wrap the task into a
Commandobject (in Java, often implementingRunnableorCallable) and drop it into a PriorityQueue. - The Result: Worker threads pick up these “Command” objects and execute them whenever they have the capacity.
3. Text Editors (The Undo/Redo Engine)
Software like IntelliJ, VS Code, or Microsoft Word relies heavily on this.
- How it works: Every action — typing a character, deleting a block, or formatting text — is a
Commandobject stored in a Stack. - Undo: Pop the last command from the “Done” stack and call its
undo()method. - Redo: Push that same command onto a “Redo” stack so it can be re-executed.
4. Transaction Management (Databases & Banking)
When performing operations that must be “all or nothing,” the Command pattern tracks the steps.
- The Use Case: A bank transfer involves (1) deducting from Account A and (2) adding to Account B.
- The Logic: Each step is wrapped in a command. If step 2 fails, the system iterates through the history of commands and calls
undo()on everything that already succeeded to prevent data corruption.
When to use it vs. a simple Method Call?
Use Command if you find yourself writing massive switch or if-else blocks to decide which action to take based on a user input. If you just need to call a method and move on, stick to the simple way—don't over-engineer!
Example in Java: Undo/Redo
Using the Command Pattern turns actions into first-class objects, which is exactly how IDEs and word processors manage complex state changes without losing their minds.
Here is a breakdown of how this looks in Java, specifically focusing on the “Undo/Redo” mechanism.
1. The Command Interface
First, we define a common interface. Every action must know how to execute and, crucially, how to reverse itself.
public interface Command {
void execute();
void undo();
}
2. The Concrete Command (The “Action”)
Imagine a simple Document class that holds text. A WriteCommand would look like this:
public class WriteCommand implements Command {
private Document doc;
private String textAdded;
public WriteCommand(Document doc, String textAdded) {
this.doc = doc;
this.textAdded = textAdded;
}
@Override
public void execute() {
doc.appendText(textAdded);
}
@Override
public void undo() {
doc.removeLastChars(textAdded.length());
}
}
3. The Invoker (The Manager)
This is the “Brain” that manages the stacks. It doesn’t need to know what the commands do, only that they can be pushed, popped, and executed.
import java.util.Stack;
public class EditorInvoker {
private Stack<Command> undoStack = new Stack<>();
private Stack<Command> redoStack = new Stack<>();
public void executeCommand(Command cmd) {
cmd.execute();
undoStack.push(cmd);
redoStack.clear(); // New actions usually clear the redo history
}
public void undo() {
if (!undoStack.isEmpty()) {
Command cmd = undoStack.pop();
cmd.undo();
redoStack.push(cmd);
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command cmd = redoStack.pop();
cmd.execute();
undoStack.push(cmd);
}
}
}
4. The Receiver: Document
This class contains the actual “state” and the logic for manipulating that state.
public class Document {
private StringBuilder content = new StringBuilder();
public void appendText(String text) {
content.append(text);
System.out.println("Current Doc: " + content.toString());
}
public void removeLastChars(int count) {
int len = content.length();
if (count <= len) {
content.delete(len - count, len);
}
System.out.println("Current Doc: " + content.toString());
}
public String getContent() {
return content.toString();
}
}
5. Client
public class Main {
public static void main(String[] args) {
// 1. Create the Receiver
Document myDoc = new Document();
// 2. Create the Invoker
EditorInvoker editor = new EditorInvoker();
// 3. Client creates Concrete Commands and associates them with the Receiver
Command step1 = new WriteCommand(myDoc, "Hello ");
Command step2 = new WriteCommand(myDoc, "World!");
// 4. Client tells Invoker to execute
System.out.println("--- Executing ---");
editor.executeCommand(step1);
editor.executeCommand(step2);
// 5. Test Undo/Redo
System.out.println("--- Undoing ---");
editor.undo(); // Removes "World!"
System.out.println("--- Redoing ---");
editor.redo(); // Puts "World!" back
} 메타데이터
- post_id
- 7cd99e4e2eb8
- slug
- command-design-pattern-7cd99e4e2eb8
- url
- https://medium.com/@kandaanusha/command-design-pattern-7cd99e4e2eb8
- canonical_url
- https://medium.com/@kandaanusha/command-design-pattern-7cd99e4e2eb8
- author_url
- https://medium.com/@kandaanusha
- status
- ok
- fetched_at
- 2026-08-16 14:54:38