Command Design Pattern in Java
Encapsulate a request as an object so it can be executed, queued, logged, or undone.
What is Command?
The Command pattern turns a request into a separate object. The sender, such as a remote control, only knows how to execute a command. It does not need to know how the receiver, such as a television, performs the operation. This decoupling makes commands easier to add, reuse, queue, test, and undo.
Beginner-friendly Java example
Focus on the roles in the example first. The pattern becomes easier when you can identify the sender, receiver, context, state, or strategy involved.
// Step 1: Define the Command interface
interface Command {
void execute();
}
// Step 2: Implement concrete commands
class TurnOnCommand implements Command {
private ElectronicDevice device;
public TurnOnCommand(ElectronicDevice device) {
this.device = device;
}
@Override
public void execute() {
device.turnOn();
}
}
class TurnOffCommand implements Command {
private ElectronicDevice device;
public TurnOffCommand(ElectronicDevice device) {
this.device = device;
}
@Override
public void execute() {
device.turnOff();
}
}
// Step 3: Define the Receiver
class ElectronicDevice {
private String name;
public ElectronicDevice(String name) {
this.name = name;
}
public void turnOn() {
System.out.println(name + " is turned on.");
}
public void turnOff() {
System.out.println(name + " is turned off.");
}
}
// Step 4: Create the Invoker
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// Step 5: Test the implementation
public class Main {
public static void main(String[] args) {
ElectronicDevice tv = new ElectronicDevice("TV");
Command turnOnCommand = new TurnOnCommand(tv);
Command turnOffCommand = new TurnOffCommand(tv);
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(turnOnCommand);
remoteControl.pressButton();
remoteControl.setCommand(turnOffCommand);
remoteControl.pressButton();
}
}Benefits and trade-offs
Benefits
- Keeps responsibilities focused.
- Reduces conditional and tightly coupled code.
- Makes behavior easier to extend and test.
Trade-offs
- Can introduce extra objects and interfaces.
- Too many small classes can make simple logic harder to follow.
Real-time use cases
- Remote-control buttons
- Undo and redo actions
- Job and message queues
- GUI buttons and menus
- Audit logging and retry workflows
Key takeawayEncapsulate a request as an object so it can be executed, queued, logged, or undone. Use it when behavior or communication is changing faster than the objects themselves.
