Design Patterns
- Command Pattern
Intent
Encapsulates a request as an object, thereby letting us parameterize other objects with different requests, queue or log requests, and support undoable operations.
The Command object has the key of the pattern.
For example, LightOnCommand object has the right method (execute(), and actually, doing light->On()) of the receiver (Light object). This is possible, the command object has the pointer to the Light receiver as its member. So, when asked to perform an action on a receiver, we just feeding the command object to the invoker.
The invoker which is named as control in the code make a request of a Command object by calling that object's execute() method. Then, since the method knows what the receiver is, it invokes the action on that receiver.
Here is an example code:
#include <iostream> using namespace std; // Command Interface class Command { public: virtual void execute() = 0; }; // Receiver Class class Light { public: void on() { cout << "The light is on\n"; } void off() { cout << "The light is off\n"; } }; // Command for turning on the light class LightOnCommand : public Command { public: LightOnCommand(Light *light) : mLight(light) {} void execute(){ mLight->on(); } private: Light *mLight; }; // Command for turning off the light class LightOffCommand : public Command { public: LightOffCommand(Light *light) : mLight(light) {} void execute(){ mLight->off(); } private: Light *mLight; }; // Invoker // Stores the ConcreteCommand object class RemoteControl { public: void setCommand(Command *cmd) { mCmd = cmd; } void buttonPressed() { mCmd->execute(); } private: Command *mCmd; }; // The client int main() { // Receiver Light *light = new Light; // concrete Command objects LightOnCommand *lightOn = new LightOnCommand(light); LightOffCommand *lightOff = new LightOffCommand(light); // invoker objects RemoteControl *control = new RemoteControl; // execute control->setCommand(lightOn); control->buttonPressed(); control->setCommand(lightOff); control->buttonPressed(); delete light, lightOn, lightOff, control; return 0; }
Output from the run:
The light is on The light is off
We are not there yet, but let's summarize what's been done.
We know that a command object encapsulates a request by binding together
a set of actions on a specific receiver (Light).
So, a command object packages the actions (on/off) and the receiver (Light) up into an object (LightOn/OffCommand) that exposes just one method, execute().
The key to this pattern is an abstract Command class, which declares an interface for executing operations. In the simplest form this interface includes an abstract Execution operation:
// Command Interface class Command { public: virtual void execute()=0; };
Concrete Command subclasses (LightOnCommand/LightOffCommand) specify a receiver-action pair by storing the receiver (Light) as an instance variable (mLight) and by implementing execute() to invoke the request. The receiver has the knowledge required to carry out the request.
class LightOnCommand : public Command { public: LightOnCommand(Light *light) : mLight(light) {} void execute(){ mLight->on(); } private: Light *mLight; };
When called, execute() causes the actions to be invoked on the receiver.
(a) control->setCommand(lightOn) (b) control->buttonPressed() (c) RemoteControl::mCmd->execute() (d) LightOnCommand::execute() (e) mLight->on()
Here, note that mCmd is a pointer to the Concrete-Command object:
Command* mCmd;
From the outside, no other objects really know what actions get performed on what receiver. They just know that if they call the execute() method, their request will be served.
The simple example of the previous section has been extended so that we can handle several receivers. The commands bounded for each receivers are stored in vector form:
vector<Command*> mOnCommand, mOffCommand;
Also, so called Null Object pattern has been added.
#include <iostream> #include <vector> using namespace std; const int MaxCommand = 5; enum Receiver { LIGHT = 0, FAN, DOOR, OVEN, NONE }; // Command Interface class Command { public: virtual void execute() = 0; }; // Receiver Class class Light { public: void on() { cout << "The light is on\n"; } void off() { cout << "The light is off\n"; } }; // Receiver Class class Fan { public: void on() { cout << "The fan is on\n"; } void off() { cout << "The fan is off\n"; } }; // Command for turning on the light class NullCommand : public Command { public: void execute(){ cout << "Null command: does nothing\n"; } }; // Command for turning on the light class LightOnCommand : public Command { public: LightOnCommand(Light *light) : mLight(light) {} void execute(){ mLight->on(); } private: Light *mLight; }; // Command for turning off the light class LightOffCommand : public Command { public: LightOffCommand(Light *light) : mLight(light) {} void execute(){ mLight->off(); } private: Light *mLight; }; // Command for turning on the fan class FanOnCommand : public Command { public: FanOnCommand(Fan *fan) : mFan(fan) {} void execute(){ mFan->on(); } private: Fan *mFan; }; // Command for turning off the fan class FanOffCommand : public Command { public: FanOffCommand(Fan *fan) : mFan(fan) {} void execute(){ mFan->off(); } private: Fan *mFan; }; // Invoker // Stores the ConcreteCommand object class RemoteControl { public: RemoteControl() : mOnCommand(MaxCommand), mOffCommand(MaxCommand) { Command *nullCmd = new NullCommand; for(int i = 0; i < MaxCommand; i++) { mOffCommand[i] = nullCmd; mOnCommand[i] = nullCmd; } } void setCommand(Receiver id, Command *onCmd, Command *offCmd) { mOnCommand[id] = onCmd; mOffCommand[id] = offCmd; } void onButtonPressed(Receiver id) { mOnCommand[id]->execute(); } void offButtonPressed(Receiver id) { mOffCommand[id]->execute(); } private: vector<Command*> mOnCommand, mOffCommand; }; // The client int main() { // Receiver Light *light = new Light; Fan *fan = new Fan; // concrete Command objects LightOnCommand *lightOn = new LightOnCommand(light); LightOffCommand *lightOff = new LightOffCommand(light); FanOnCommand *fanOn = new FanOnCommand(fan); FanOffCommand *fanOff = new FanOffCommand(fan); NullCommand *nullOn = new NullCommand(); NullCommand *nullOff = new NullCommand(); // invoker objects RemoteControl *control = new RemoteControl; // execute control->setCommand(LIGHT, lightOn, lightOff); control->onButtonPressed(LIGHT); control->offButtonPressed(LIGHT); control->setCommand(FAN, fanOn, fanOff); control->onButtonPressed(FAN); control->offButtonPressed(FAN); control->setCommand(NONE, nullOn, nullOff); control->onButtonPressed(NONE); delete light, lightOn, lightOff; delete fan, fanOn, fanOff; delete control; return 0; }
Output:
The light is on The light is off The fan is on The fan is off Doing nothing
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization