This post implements the command pattern in the book <Head First Design Patterns>. TheCommand Pattern encapsulates a request as an object, thereby, letting you parametrize other objects with different requests, queue or log requests, and support undoable operations.
The command object 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 exposes just one method,execute(). When called, execute() causes the actions to be invoked on the receiver. From the outside, no other objects really know what actions get performed on what receiver. They just know that if they call theexecute() method, their request will be served.
A command is a method call wrapped in an object. Commands are an object-oriented replacement for callbacks. The basic procedure for command pattern is
- Define a Command interface with a method signature like
execute()
. - Create one or more derived classes that encapsulate some subsetof the following: a "receiver" object, the method to invoke, the arguments to pass.
- Instantiate a Command object for each deferred execution request.
- Pass the Command object from the creator (aka sender) to the invoker (aka receiver).
- The invoker decides when to
execute()
.
Let's implement the example in the book. First we define vendor's classes.
class Light{
private:
std::string location;
public:
Light(const std::string location_): location(location_){}
void on() const{
std::cout << location.c_str() << " light is on" << std::endl;
}
void off() const {
std::cout << location.c_str() << " light is off" << std::endl;
}
};
class TV{
private:
mutable int channel;
std::string location;
public:
TV(const std::string location_):channel(0), location(location_){}
void on() const{
std::cout << location.c_str() << " TV is on" << std::endl;
}
void off() const{
std::cout << location.c_str() << " TV is off" << std::endl;
}
void setInputChannel(){
channel = 3;
std::cout << location.c_str() << " Channel is set for VCR" << std::endl;
}
};
class Stereo{
private:
std::string location;
public:
Stereo(std::string location):location(location_){}
void on() const {
std::cout << location.c_str() << " stereo is on" << std::endl;
}
void off() const {
std::cout << location.c_str() << " stereo is off" << std::endl;
}
void setCD() const {
std::cout << location.c_str() << " stereo is set for CD input" << std::endl;
}
void setDVD() const {
std::cout << location.c_str() << " stereo is set for DVD input" << std::endl;
}
void setRadio() const {
std::cout << location.c_str() << " stereo is set for Radio" << std::endl;
}
void setVolume(int volume) const {
// code to set the volume
// valid range: 1-11 (after all 11 is better than 10, right?)
std::cout << location.c_str() << " Stereo volume set to " << volume << std::endl;
}
};
class Hottub{
private:
mutable bool on;
mutable int temperature;
public:
Hottub():on(false), temperature(0){}
void hottubon() const { on = true; }
void hottuboff() const { on = false; }
void bubblesOn() const {
if(on){
std::cout << "Hottub is bubbling!" << std::endl;
}
}
void bubblesOff() const {
if(on){
std::cout << "Hottub is not bubbling" << std::endl;
}
}
void jetsOn() const {
if(on){
std::cout << "Hottub jets are on" << std::endl;
}
}
void jetsOff() const {
if(on) {
std::cout << "Hottub jets are off" << std::endl;
}
}
void setTemperature(int temperature_) {
temperature = temperature_;
}
void heat() const {
temperature = 105;
std::cout << "Hottub is heating to a steaming 105 degrees" << std::endl;
}
void cool() const {
temperature = 98;
std::cout << "Hottub is cooling to 98 degrees" << std::endl;
}
};
class GarageDoor {
private:
std::string location;
public:
GarageDoor(const std::string location_):location(location_) {}
void up() const {
std::cout << location.c_str() << " Door is Up" << std::endl;
}
void down() const {
std::cout << location.c_str() << " Door is Down" << std::endl;
}
void stop() const {
std::cout << location.c_str() << " Door is Stopped" << std::endl;
}
void lightOn() const {
std::cout << location.c_str() << " light is on" << std::endl;
}
void lightOff() const {
std::cout << location.c_str() << " light is off" << std::endl;
}
};
class CeilingFan {
private:
mutable int level;
std::string location;
public:
static const int HIGH = 2;
static const int MEDIUM = 1;
static const int LOW = 0;
CeilingFan(const std::string location_):level(LOW), location(location_) {}
void high() const {
level = HIGH;
std::cout << location.c_str() << " ceiling fan is on high" << std::endl;
}
void medium() const {
level = MEDIUM;
std::cout << location.c_str() << " ceiling fan is on medium" << std::endl;
}
public: void low() const {
level = LOW;
std::cout << location.c_str() << " ceiling fan is on low" << std::endl;
}
public: void off() const {
level = 0;
std::cout << location.c_str() << " ceiling fan is off" << std::endl;
}
public: int getSpeed() const {
return level;
}
};
Then we define command process
class Command {
public:
virtual void execute() const = 0;
};
class LightOnCommand : public Command {
private:
const Light* light;
public:
LightOnCommand(const Light* light_) : light(light_){}
void execute() const {
light->on();
}
~LightOnCommand(){
delete light;
light = NULL;
}
};
class LightOffCommand : public Command {
private:
const Light* light;
public:
LightOffCommand(const Light* light_) : light(light_){}
void execute() const {
light->off();
}
~LightOffCommand(){
delete light;
light = NULL;
}
};
class LivingroomLightOnCommand : public Command {
private:
const Light* light;
public:
LivingroomLightOnCommand(const Light* light_):light(light_){}
void execute() const {
light->on();
}
~LivingroomLightOnCommand(){
delete light;
light = NULL;
}
};
class LivingroomLightOffCommand : public Command {
private:
const Light* light;
public:
LivingroomLightOffCommand(const Light* light_):light(light_){}
void execute() const {
light->off();
}
~LivingroomLightOffCommand(){
delete light;
light = NULL;
}
};
class NoCommand : public Command{
public:
void execute() const {};
};
The remote control class is given by
class RemoteControl {
private:
static const int SLOTS = 7;
Command* onCommands[SLOTS];
Command* offCommands[SLOTS];
Command* noCommand;
RemoteControl(const RemoteControl& ); // Disable copy constructor
void operator=(const RemoteControl& ); // Disable assignment operator
public:
RemoteControl() {
noCommand = new NoCommand();
for(int i = 0; i < SLOTS; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
~RemoteControl() {
for(int i = 0; i < SLOTS; i++) {
delete onCommands[i];
delete offCommands[i];
onCommands[i] = NULL;
offCommands[i] = NULL;
}
delete noCommand;
noCommand = NULL;
}
void setCommand(int slot, Command* onCommand, Command* offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
void onButtonWasPushed(int slot) const {
onCommands[slot]->execute();
}
void offButtonWasPushed(int slot) const {
offCommands[slot]->execute();
}
void toString() const {
std::cout << "\n------ Remote Control -------\n" << std::endl;
for( int i = 0; i < SLOTS; i++ ) {
std::cout << "[slot " << i << "] ";
std::cout << typeid( *onCommands[i] ).name();
std::cout << " ";
std::cout << typeid( *offCommands[i] ).name();
std::cout << std::endl;
}
}
};
The main function is
#include <iostream>
#include <typeinfo>
#include <./vendor.hpp>
#include <./command.hpp>
#include <./remotecontrol.hpp>
using namespace std;
int main()
{
RemoteControl* rc = new RemoteControl();
Light* livingRoomLight = new Light("Living Room");
Light* kitchenLight = new Light("Kitchen");
LightOnCommand* livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand* livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand* kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand* kitchenLightOff = new LightOffCommand(kitchenLight);
rc->setCommand( 0, livingRoomLightOn, livingRoomLightOff);
rc->setCommand( 1, kitchenLightOn, kitchenLightOff);
rc->onButtonWasPushed(0);
rc->offButtonWasPushed(0);
rc->onButtonWasPushed(1);
rc->offButtonWasPushed(1);
delete rc;
return 0;
}