Design Patterns – Command
Introduction to the Command Pattern
The Command pattern is a behavioral design pattern that encapsulates a request as an object, thereby allowing for parameterization of clients with requests, queuing of requests, and logging of the requests. This pattern decouples the sender and receiver of a command, allowing for greater flexibility and extensibility in the code.
Key Components of the Command Pattern
The Command pattern involves the following key components:
- Command: The Command is an interface or abstract class that defines the common interface for all concrete commands. It typically includes an
execute
method that encapsulates the requested action. - Concrete Command: Concrete Command classes implement the
Command
interface. They define specific actions by calling methods on the receiver (the object that performs the action). - Receiver: The Receiver is an object that performs the actual action or operation. It is unaware of the Command and the invoker, making it independent of the requestor.
- Invoker: The Invoker is responsible for invoking the Command and managing the Command objects. It sends the Command to the appropriate Receiver to perform the requested action.
- Client: The Client creates and configures the Command objects, assigns them to specific Receivers, and sets up the Invoker to execute the commands.
Implementing the Command Pattern
Let’s illustrate the Command pattern with a simple example in Java:
// Command interface
interface Command {
void execute();
}
// Receiver
class Light {
public void turnOn() {
System.out.println("Light is on");
}
public void turnOff() {
System.out.println("Light is off");
}
}
// Concrete Command: Turn On
class TurnOnLightCommand implements Command {
private Light light;
public TurnOnLightCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
// Concrete Command: Turn Off
class TurnOffLightCommand implements Command {
private Light light;
public TurnOffLightCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOff();
}
}
// Invoker
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// Client
public class CommandPatternExample {
public static void main(String[] args) {
Light light = new Light();
Command turnOn = new TurnOnLightCommand(light);
Command turnOff = new TurnOffLightCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(turnOn);
remote.pressButton();
remote.setCommand(turnOff);
remote.pressButton();
}
}
In this example, we have a Command
interface, two concrete commands (TurnOnLightCommand
and TurnOffLightCommandLight
Receiver, a RemoteControl
Invoker, and a client application that configures and uses the commands to control the light.
Usage of the Command Pattern
The Command pattern is used in various applications where you need to decouple the sender and receiver of a request. Common use cases include implementing undo/redo functionality, creating macro commands, managing transactional operations, and building extensible command-based systems.
Benefits of the Command Pattern
The Command pattern offers several advantages:
- Decoupling: It decouples the sender from the receiver, making it easier to add new commands and extend functionality.
- Undo/Redo: It simplifies the implementation of undo/redo functionality by storing command history.
- Queueing: Commands can be queued and executed in a specific order, allowing for complex scenarios like batch processing.
- Logging: Commands can be logged for auditing, debugging, or analysis purposes.
Comparison with Other Patterns
The Command pattern is often compared to the Chain of Responsibility and the Mediator patterns. While all three patterns deal with reducing coupling, the Command pattern focuses on encapsulating a request as an object, the Chain of Responsibility pattern deals with passing a request along a chain of handlers, and the Mediator pattern defines an object that centralizes communication between objects.
Conclusion
The Command pattern is a valuable design pattern for achieving loose coupling between the sender and receiver of requests. It provides a clean and extensible way to encapsulate and parameterize commands, enabling features like undo/redo, macro recording, and more in your Java applications.