Command Design Pattern in Java
1. Introduction
In many applications, we want to issue requests without knowing exactly who will handle them or how they will be executed. The Command Design Pattern solves this by encapsulating a request into a separate object.
This pattern helps in:
- Decoupling the sender (who issues the request) from the receiver (who performs the action).
- Supporting undo/redo operations.
- Queueing or logging requests.
2. Real-Life Analogy
Think of a remote control:
- The remote (Invoker) does not know how the TV, fan, or AC actually works.
- Each button press is a command.
- The appliance (Receiver) knows how to perform the action.
The remote just sends the command; the appliance executes it.
3. Structure
- Command Interface → Declares an execute() method.
- ConcreteCommand → Implements the command, calling methods on the receiver.
- Receiver → Knows how to perform the actual operation.
- Invoker → Calls the command without knowing the details.
- Client → Creates the command and configures the invoker.
4. Custom Java Implementation
Step 1: Command Interface
interface Command {
void execute();
}
Step 2: Receiver Classes (Devices)
class Light {
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}
class Fan {
public void start() {
System.out.println("Fan started");
}
public void stop() {
System.out.println("Fan stopped");
}
}
Step 3: Concrete Commands
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
class FanStartCommand implements Command {
private Fan fan;
public FanStartCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.start();
}
}
class FanStopCommand implements Command {
private Fan fan;
public FanStopCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.stop();
}
}
Step 4: Invoker (Remote Control)
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
if (command != null) {
command.execute();
}
}
}
Step 5: Client Code
public class CommandPatternDemo {
public static void main(String[] args) {
// Receivers
Light livingRoomLight = new Light();
Fan ceilingFan = new Fan();
// Concrete commands
Command lightOn = new LightOnCommand(livingRoomLight);
Command lightOff = new LightOffCommand(livingRoomLight);
Command fanStart = new FanStartCommand(ceilingFan);
Command fanStop = new FanStopCommand(ceilingFan);
// Invoker
RemoteControl remote = new RemoteControl();
// Execute commands
System.out.println("Turning ON Light:");
remote.setCommand(lightOn);
remote.pressButton();
System.out.println("\nTurning OFF Light:");
remote.setCommand(lightOff);
remote.pressButton();
System.out.println("\nStarting Fan:");
remote.setCommand(fanStart);
remote.pressButton();
System.out.println("\nStopping Fan:");
remote.setCommand(fanStop);
remote.pressButton();
}
}
5. Output
Turning ON Light:
Light is ON
Turning OFF Light:
Light is OFF
Starting Fan:
Fan started
Stopping Fan:
Fan stopped
6. Benefits
- Loose coupling: The invoker doesn’t know how the receiver performs the action.
- Flexibility: Easy to add new commands without changing existing code.
- Supports undo/redo by keeping track of executed commands.
- Useful in queues, logs, and macro commands.
7. When to Use
- When requests need to be parameterized as objects.
- When you want to support undo/redo functionality.
- When you need to queue, log, or batch requests.
Next- Interpreter Design Pattern in Java