Introduction
The Command Pattern is a behavioral design pattern that encapsulates a request as an object, allowing parameterization of clients with queues, undo/redo operations, and logging of actions.
Why Command Pattern Matters
It decouples the object that invokes a command from the one that knows how to execute it. This is useful for executing commands later, storing history, batching tasks, or building macro commands.
Core Intent and Participants
- Intent: Encapsulate a request as an object, thereby allowing for parameterization of clients with different requests, queuing, and logging.
Participants
Command
: Declares the execution interface.ConcreteCommand
: Implements the command and delegates to a receiver.Receiver
: Knows how to perform the work.Invoker
: Triggers the command.Client
: Creates and configures commands.
UML Diagram (Text)
+---------+ +----------------+ +---------------+
| Client |----->| ConcreteCommand|----->| Receiver |
+---------+ +----------------+ +---------------+
^
|
+------------+
| Command |
+------------+
^
|
+------------+
| Invoker |
+------------+
Real-World Use Cases
- Remote controls (on/off, volume, channel)
- GUI actions (undo, redo, copy, paste)
- Task queues and schedulers
- Macro recording systems
- Game development (character actions, input mapping)
- Workflow and rule engines
Java Implementation Strategy
Example: Remote Control
Step 1: Command Interface
public interface Command {
void execute();
}
Step 2: Receiver
public class Light {
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}
Step 3: Concrete Commands
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOff();
}
}
Step 4: Invoker
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
Step 5: Client Code
public class CommandPatternDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();
remote.setCommand(lightOff);
remote.pressButton();
}
}
✅ You can queue, undo, or log commands thanks to this structure.
Pros and Cons
✅ Pros
- Decouples sender and receiver
- Supports undo, logging, and queuing of requests
- Adds extensibility without modifying existing code
❌ Cons
- Can result in a large number of small classes
- Slightly more complex architecture for simple actions
Anti-Patterns and Misuse
- Using Command where simple method invocation suffices
- Mixing logic into the invoker (violates SRP)
- Not separating receiver logic from command
Command vs Strategy vs Observer
Pattern | Goal | Encapsulates | Primary Use Case |
---|---|---|---|
Command | Encapsulate and defer requests | Action/Request | Undo, logging, macros |
Strategy | Encapsulate algorithms | Behavior | Sorting, calculation logic |
Observer | Event-driven notifications | State change | Event handling, GUIs |
Refactoring Legacy Code
Before
light.turnOn();
light.turnOff();
After (Using Command)
remote.setCommand(new LightOnCommand(light));
remote.pressButton();
✅ Enables reusability, undo, logging, and more.
Best Practices
- Use command interface consistently across all commands
- Favor immutability for command data (e.g., command parameters)
- Batch and queue commands where needed (e.g., macro replay)
- Use lambdas in Java 8+ to simplify command definitions
Real-World Analogy
Think of a restaurant waiter. You (client) place an order (command), the waiter (invoker) delivers it to the kitchen (receiver), which prepares the meal. The chef doesn’t know who ordered, just what to cook. The system is clean and decoupled.
Java Version Relevance
- Java 8+: Use lambdas as commands for lightweight syntax
- JavaFX: Uses command pattern for UI actions
- Java Concurrency:
Runnable
andCallable
are command-style interfaces
Conclusion & Key Takeaways
- Command Pattern turns actions into objects that can be passed, queued, or undone.
- Perfect for GUIs, input handlers, macro recording, and task queues.
- Decouples sender from receiver and enhances maintainability.
- Can be simplified with Java 8+ lambda expressions.
FAQ – Command Pattern in Java
1. What is the Command Pattern?
A pattern that encapsulates a request as an object.
2. When should I use it?
When you want to support undo, queuing, or execute commands at different times.
3. Is Command Pattern decoupled?
Yes. It separates the sender from the receiver.
4. Can I implement undo with this?
Yes. Store command history and implement a undo()
method.
5. Can it be used in real-time apps?
Yes. Especially for game actions and UI input handling.
6. What’s the difference from Strategy Pattern?
Command encapsulates an action/request. Strategy encapsulates behavior/algorithm.
7. Is it used in Java standard libraries?
Yes. Runnable
, Callable
, and ActionListener
use this concept.
8. Can commands be asynchronous?
Yes. Combine with executor services for async execution.
9. How do I log commands?
Store command objects in a list before executing.
10. Can I batch multiple commands together?
Yes. Use a MacroCommand
that holds a list of commands.