1. What Command Pattern defined
Four terms are always associated with the command pattern, they are command, receiver, invoker and client. Let’s see them in detail.
Image source: Head First Design Patterns
1). Command object
It encapsulates a request by binding together a set of actions on a specific receiver. To achieve this, it packages the actions and the receiver up into an object that expoased just one method, execute()
. When called, execute()
causes the actions to be invoked on the receiver.
From the outside, no other objects really know that actions get performed on what receiver; they just know that if they call execute()
method, their request will be serviced.
2). Receiver object:
The receiver object is also stored in command object. The recever does the work when the execute()
method in command is called.
3). Invoker object:
An invoker object knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker does not know anything about a concrete command, it knows only about the command interface.
4). Clinet object:
Invoker, command, and receiver objects are held by a client object, client decides which receiver it assigns to the command, and which command it assigns to the invoker. The client decides which commands to execute at which points. To execute a command, it passes the command to the invoker.
2. Application scenario
Command design pattern can solve these problems:
- Coupling the invoker of a request to a particular request should be avoided.
- It should be possible to configure an object (that invokes a request) with a request.
The solutions it provides are:
- Define seperate (command) objects that encapsulate a request.
- A class delegates a request to a common object instead of implementing a particular request directly.
3. Structure
Image source: Head First Design Patterns
4. Example
In this example, the task is to design an API for remote control of some devices in home like light, garage door… When user click a light on button then the light is turned on.
EXAMPLE
/*-----------entities----------------*/
/*
* Light has two function, turn on or turn off
* */
public class Light {
public void on() {
System.out.println("Turn light on.");
}
public void off() {
System.out.println("Turn light off.");
}
}
/*-----------command interface----------------*/
/*
* Command Interface
* */
public interface Command {
public void execute();
}
/*-----------concrete command class------------*/
/*
* Command to turn on light
* */
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
/*
* Command to turn off light
* */
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
/*-------------------invoker-------------------*/
/*
* invoker: used to set commands
* */
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
public RemoteControl() {
this.onCommands = new Command[2];
this.offCommands = new Command[2];
Command noCommand = new NoCommand();
for (int i = 0; i < 2; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
public void setCommands(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("\n---------- Remote Control --------\n");
for (int i = 0; i < onCommands.length; i++)
sb.append("slot: " + i + ", " + onCommands[i].getClass().getName() +
" " + offCommands.getClass().getName() + "\n");
return sb.toString();
}
}
/*-------------------client-------------------*/
/*
* client: hold the command, receiver, and invoker objects
* */
public class RemoteLoader {
public static void main(String[] args) {
// Create all the devices
Light light = new Light();
// Create all the light command objects
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
// load commands to the remote slots
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommands(0, lightOnCommand, lightOffCommand);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
}
}
The result is shown as follows.
5. Conclusion
- The Command Pattern decouples an object, making a request from the one that knows how to perform it.
- The command object is at the center of this decoupling and encapsulates a receiver with an action (or set of actions).
- An invoker makes a request of a Command object by calling its
execute()
method, which invokes those actions on the receiver. - Invokers can be parameterized with Coammands, even dynamically at runtime.
- Support Undo.