Command Pattern
Command Pattern → encapsulate request as object → caller can execute, queue, retry, log, or undo request without knowing implementation details.
create()getInstance()process()- “I will apply Command Pattern here to encapsulate changes and satisfy Open/Closed principle.”
- “De-couples core execution from specific implementations.”
Command Pattern
Command Pattern
1. ONE-LINE IDEA
Command Pattern → encapsulate request as object → caller can execute, queue, retry, log, or undo request without knowing implementation details.
Example: Remote control sends commands to TV.
2. PROBLEM
Java
if (action.equals("ON")) {
tv.turnOn();
} else if (action.equals("OFF")) {
tv.turnOff();
} else if (action.equals("VOLUME_UP")) {
tv.increaseVolume();
}Problems:
- Remote tightly coupled to TV.
- New action → modify remote logic.
- Hard to queue, log, or undo actions.
- Different devices require different conditionals.
Solution: Each request becomes its own command object.
3. REQUIREMENTS
Functional:
- Execute different device operations through common interface.
- Support multiple devices.
- Allow caller to invoke commands without knowing receiver.
- Support undo for reversible operations.
- Allow commands to be swapped dynamically.
Out of scope: Persistent command queue, distributed execution, crash recovery.
4. CORE ENTITIES
Command→ commonexecute()contract.LightOnCommand/LightOffCommand→ concrete commands.Light→ receiver; performs actual work.RemoteControl→ invoker; triggers command.Main→ client; wires objects together.
5. UML CLASS DIAGRAM
Client
|
| configures
v
RemoteControl (Invoker)
|
| has
v
Command (Interface)
^
|
+-------------------+
| |
LightOnCommand LightOffCommand
| |
+---------+---------+
|
v
Light
(Receiver)Relationships:
RemoteControl→ association withCommand.- Concrete commands → realize
Command. - Concrete commands → hold reference to receiver
Light. - Client wires invoker + command + receiver.
6. FULL JAVA 17 IMPLEMENTATION
Java
interface Command {
void execute();
void undo();
}
final class Light {
private boolean on;
public void turnOn() {
on = true;
System.out.println("Light ON");
}
public void turnOff() {
on = false;
System.out.println("Light OFF");
}
public boolean isOn() {
return on;
}
}
final class LightOnCommand implements Command {
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff();
}
}
final class LightOffCommand implements Command {
private final Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
@Override
public void undo() {
light.turnOn();
}
}
final class RemoteControl {
private Command command;
private Command lastExecuted;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
if (command == null) {
throw new IllegalStateException(
"No command configured"
);
}
command.execute();
lastExecuted = command;
}
public void undoLastCommand() {
if (lastExecuted == null) {
System.out.println("Nothing to undo");
return;
}
lastExecuted.undo();
lastExecuted = null;
}
}
public class Main {
public static void main(String[] args) {
Light light = new Light();
Command on = new LightOnCommand(light);
Command off = new LightOffCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(on);
remote.pressButton();
remote.undoLastCommand();
remote.setCommand(off);
remote.pressButton();
}
}Output:
Light ON
Light OFF
Light OFF7. EXECUTION FLOW
Client configures command
|
v
RemoteControl.setCommand(command)
|
v
RemoteControl.pressButton()
|
v
Command.execute()
|
v
Light.turnOn()Invoker doesn't know how light turns on. It only knows Command.
8. UNDO — IMPORTANT DETAIL
Simple undo() works here because operations are reversible and inverse is obvious.
For general commands, store previous state:
Java
final class SetVolumeCommand implements Command {
private final Stereo stereo;
private final int newVolume;
private int previousVolume;
public SetVolumeCommand(Stereo stereo, int newVolume) {
this.stereo = stereo;
this.newVolume = newVolume;
}
@Override
public void execute() {
previousVolume = stereo.getVolume();
stereo.setVolume(newVolume);
}
@Override
public void undo() {
stereo.setVolume(previousVolume);
}
}Assumes Stereo provides getVolume() and setVolume(int).
Caveat: Current remote remembers only last command. Real undo/redo needs command history stack; repeated execution may require separate history entries.
9. DESIGN PATTERNS USED TOGETHER
- Command + Composite: macro command executes multiple commands.
- Command + Memento: save previous state for undo.
- Command + Queue: enqueue requests for async execution.
- Command + Strategy: both encapsulate behavior, but intent differs.
Command vs Strategy
| Command | Strategy |
|---|---|
| Represents request/action | Represents algorithm |
Often has execute() / undo() | Usually has operation-specific method |
| Supports queue, history, logging | Swaps behavior/algorithm |
Example: TurnOnCommand | Example: PaymentStrategy |
10. INTERVIEW QUESTIONS
Q: Why not call receiver directly?
Invoker becomes coupled to receiver. Command isolates request from execution.
Q: Who creates commands?
Usually client or DI/configuration layer.
Q: Can commands be queued?
Yes. Store Command objects in queue and execute them via worker.
Q: Can commands be retried safely?
Only if retry semantics are safe. For payments or external APIs, use idempotency keys and durable execution state.
Q: Is Command Pattern thread-safe?
Not automatically. Shared receiver state, mutable commands, and command history need synchronization where concurrent access exists.
Q: Real-world uses?
- GUI button actions
- Undo/redo editors
- Job queues
- Transaction workflows
- Smart-home controls
- Task scheduling
11. COMPLEXITY
For this implementation:
setCommand()→ O(1)O(1)O(1)pressButton()→ O(1)O(1)O(1), excluding receiver workundoLastCommand()→ O(1)O(1)O(1), excluding undo work- Space → O(1)O(1)O(1) for current/last command refs
Command history stack would use O(H)O(H)O(H) space for HHH recorded commands.
12. QUICK REVISION
- Command → request object.
- Receiver → performs work.
- Invoker → triggers command.
- Client → wires objects.
execute()→ perform action.undo()→ reverse action where possible.- Queue/history → major benefit.
13. MEMORY TRICK
Client creates → Invoker triggers → Command delegates → Receiver acts.