Overview
The Command Pattern is a behavioral design pattern that encapsulates a request as an object, thereby allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations.
This pattern decouples the sender (invoker) of a request from its receiver by introducing an intermediary object, the command, which encapsulates the details of the request.
Key Participants
Command
Declares the interface for executing an operation (execute()).
ConcreteCommand
Implements the Command interface by binding a receiver to an action.
Calls the receiver’s action in its execute() method.
Receiver
Performs the actual work when the execute() method is called on the command.
Invoker
Stores the command and triggers its execution.
Maintains a history of commands for undo/redo functionality.
Client
Creates specific commands and assigns them to the invoker.
Implementation in Code
Example: Smart Home Automation
// Command Interface
interface Command {
void execute();
void undo(); // Optional, for undoable operations
}
// Receiver
class Light {
public void turnOn() {
System.out.println("The light is ON");
}
public void turnOff() {
System.out.println("The light is OFF");
}
}
// ConcreteCommand
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff();
}
}
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
@Override
public void undo() {
light.turnOn();
}
}
// Invoker
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
// Client
public class CommandPatternDemo {
public static void main(String[] args) {
// Receiver
Light livingRoomLight = new Light();
// ConcreteCommands
Command lightOn = new LightOnCommand(livingRoomLight);
Command lightOff = new LightOffCommand(livingRoomLight);
// Invoker
RemoteControl remote = new RemoteControl();
// Turn the light ON
remote.setCommand(lightOn);
remote.pressButton();
// Undo the operation
remote.pressUndo();
// Turn the light OFF
remote.setCommand(lightOff);
remote.pressButton();
// Undo the operation
remote.pressUndo();
}
}
Key Methods
execute(): Executes the encapsulated request.
undo() (Optional): Reverts the operation, enabling undo functionality.
Advantages
Decoupling: Decouples the sender of the request (Invoker) from its receiver.
Extensibility: New commands can be added without changing existing code.
Undo/Redo: Enables history of commands for undo/redo functionality.
Macro Commands: Allows grouping multiple commands into a single macro command.
Disadvantages
Command Explosion: A large number of commands may be required for complex systems.
Overhead: Adds extra layers of abstraction, potentially increasing complexity.
Real-World Examples
GUI Buttons: Each button click triggers a specific command (e.g., save, delete, print).
Smart Home Devices: Commands to control lights, fans, or thermostats via a central controller.
Undo/Redo in Applications: Text editors and drawing applications where user actions can be reversed.
When to Use the Command Pattern?
To encapsulate requests as objects, allowing parameterization and delayed execution.
To implement undo/redo functionality.
To decouple sender and receiver objects.
To create a history of operations.