命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
命令模式将发出请求的对象和执行请求的对象解耦。
在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。
调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用。
调用者可以接受命令当作参数,甚至在运行时动态地进行。
命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态。
宏命令是命令的一种简单的延伸,允许调用多个命令。
命令也可以用来实现日志和事务系统。
命令可以将运算快打包(一个接收者一组动作),然后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的特性衍生一些应用,例如:日程安排(Scheduler)、线程池、工作队列等。
某些应用需要我们将所有的动作都记录在日志中,并能在系统死机之后,重新调用这些动作恢复到之前的状态。通过新增两个方法(store()、load()),命令模式就能够支持这一点。
当我们执行命令的时候,将历史记录储存在磁盘中。一旦系统死机,我们就可以将命令对象重新加载,并成批地依次调用这些对象的execute()方法。
比方说,对于电子表格应用,我们可能想要实现的 错误恢复方式是将电子表格的才做记录在日志中,而不是每次电子表格一有变化就记录整个电子表格。
五花八门的厂商类
#include <iostream>
#include <string>
#include <vector>
class Light {
private:
std::string location;
int level;
public:
Light(std::string loc) {
location = loc;
}
void on() {
level = 100;
printf("Light is on\n");
}
void off() {
level = 0;
printf("Light is off\n");
}
void dim(int le) {
level = le;
if (level == 0) {
off();
}
else {
printf("Light is dimmed to %d %%\n", level);
}
}
int getLevel() {
return level;
}
};
class CeilingFan {
private:
std::string location = "";
int level;
public:
static const int HIGH = 3;
static const int MEDIUM = 2;
static const int LOW = 1;
static const int OFF = 0;
CeilingFan(std::string loc) {
location = loc;
level = OFF;
}
void high() {
// turns the ceiling fan on to high
level = HIGH;
printf("%s ceiling fan is on high\n", location.c_str());
}
void medium() {
// turns the ceiling fan on to medium
level = MEDIUM;
printf("%s ceiling fan is on medium\n", location.c_str());
}
void low() {
// turns the ceiling fan on to low
level = LOW;
printf("%s ceiling fan is on low\n", location.c_str());
}
void off() {
// turns the ceiling fan off
level = OFF;
printf("%s ceiling fan is off\n", location.c_str());
}
int getSpeed() {
return level;
}
};
class GarageDoor {
private:
std::string location;
public:
GarageDoor(std::string loc) {
location = loc;
}
void up() {
printf("%s garage Door is Up\n", location.c_str());
}
void down() {
printf("%s garage Door is Down\n", location.c_str());
}
void stop() {
printf("%s garage Door is Stopped\n", location.c_str());
}
void lightOn() {
printf("%s garage light is on\n", location.c_str());
}
void lightOff() {
printf("%s garage light is off\n", location.c_str());
}
};
class Stereo {
private:
std::string location;
public:
Stereo(std::string loc) {
location = loc;
}
void on() {
printf("%s stereo is on\n", location.c_str());
}
void off() {
printf("%s stereo is off\n", location.c_str());
}
void setCD() {
printf("%s stereo is set for CD input\n", location.c_str());
}
void setDVD() {
printf("%s stereo is set for DVD input\n", location.c_str());
}
void setRadio() {
printf("%s stereo is set for Radio\n", location.c_str());
}
void setVolume(int volume) {
// code to set the volume
// valid range: 1-11 (after all 11 is better than 10, right?)
printf("%s stereo volume set to %d\n", location.c_str(), volume);
}
};
定义命令接口
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
};
实现成命令
class LightOnCommand :public Command {
private:
Light *light;
public:
LightOnCommand(Light *li) {
light = li;
}
void execute() {
light->on();
}
void undo() {
light->off();
}
};
class LightOffCommand :public Command {
private:
Light* light;
public:
LightOffCommand(Light* li) {
light = li;
}
void execute() {
light->off();
}
void undo() {
light->on();
}
};
class CeilingFanHighCommand :public Command {
private:
CeilingFan* ceilingFan;
int prevSpeed;
public:
CeilingFanHighCommand(CeilingFan* cf) {
ceilingFan = cf;
}
void execute() {
prevSpeed = ceilingFan->getSpeed();
ceilingFan->high();
}
void undo() {
if (prevSpeed == CeilingFan::HIGH){
ceilingFan->high();
}
else if (prevSpeed == CeilingFan::MEDIUM) {
ceilingFan->medium();
}
else if (prevSpeed == CeilingFan::LOW) {
ceilingFan->low();
}
else if (prevSpeed == CeilingFan::OFF) {
ceilingFan->off();
}
}
};
class CeilingFanLowCommand :public Command {
private:
CeilingFan* ceilingFan;
int prevSpeed;
public:
CeilingFanLowCommand(CeilingFan* cf) {
ceilingFan = cf;
}
void execute() {
prevSpeed = ceilingFan->getSpeed();
ceilingFan->low();
}
void undo() {
if (prevSpeed == CeilingFan::HIGH) {
ceilingFan->high();
}
else if (prevSpeed == CeilingFan::MEDIUM) {
ceilingFan->medium();
}
else if (prevSpeed == CeilingFan::LOW) {
ceilingFan->low();
}
else if (prevSpeed == CeilingFan::OFF) {
ceilingFan->off();
}
}
};
class CeilingFanMediumCommand :public Command {
private:
CeilingFan* ceilingFan;
int prevSpeed;
public:
CeilingFanMediumCommand(CeilingFan* cf) {
ceilingFan = cf;
}
void execute() {
prevSpeed = ceilingFan->getSpeed();
ceilingFan->medium();
}
void undo() {
if (prevSpeed == CeilingFan::HIGH) {
ceilingFan->high();
}
else if (prevSpeed == CeilingFan::MEDIUM) {
ceilingFan->medium();
}
else if (prevSpeed == CeilingFan::LOW) {
ceilingFan->low();
}
else if (prevSpeed == CeilingFan::OFF) {
ceilingFan->off();
}
}
};
class CeilingFanOffCommand :public Command {
private:
CeilingFan* ceilingFan;
int prevSpeed;
public:
CeilingFanOffCommand(CeilingFan* cf) {
ceilingFan = cf;
}
void execute() {
prevSpeed = ceilingFan->getSpeed();
ceilingFan->off();
}
void undo() {
if (prevSpeed == CeilingFan::HIGH) {
ceilingFan->high();
}
else if (prevSpeed == CeilingFan::MEDIUM) {
ceilingFan->medium();
}
else if (prevSpeed == CeilingFan::LOW) {
ceilingFan->low();
}
else if (prevSpeed == CeilingFan::OFF) {
ceilingFan->off();
}
}
};
class GarageDoorUpCommand :public Command {
private:
GarageDoor* garageDoor;
public:
GarageDoorUpCommand(GarageDoor* gd) {
garageDoor = gd;
}
void execute() {
garageDoor->up();
}
void undo() {
garageDoor->down();
}
};
class GarageDoorDownCommand :public Command {
private:
GarageDoor* garageDoor;
public:
GarageDoorDownCommand(GarageDoor* gd) {
garageDoor = gd;
}
void execute() {
garageDoor->down();
}
void undo() {
garageDoor->up();
}
};
class StereoOnWithCDCommand :public Command {
private:
Stereo* stereo;
public:
StereoOnWithCDCommand(Stereo* ster) {
stereo = ster;
}
void execute() {
stereo->on();
stereo->setCD();
stereo->setVolume(11);
}
void undo() {
stereo->off();
}
};
class StereoOffCommand :public Command {
private:
Stereo* stereo;
public:
StereoOffCommand(Stereo* ster) {
stereo = ster;
}
void execute() {
stereo->off();
}
void undo() {
stereo->on();
stereo->setCD();
stereo->setVolume(11);
}
};
使用NoCommand对象,以便确定每个slot永远都有命令对象
class NoCommand :public Command {
public:
void execute() {
printf("No Command\n");
}
void undo() {
printf("No Command\n");
}
};
定义宏命令
class MacroCommand :public Command {
private:
Command** commands;
int commands_count;
public:
MacroCommand(Command** com, int commandsNo) {
commands = com;
commands_count = commandsNo;
}
void execute() {
for (int i = 0; i < commands_count; i++) {
commands[i]->execute();
}
}
void undo() {
for (int i = 0; i < commands_count; i++) {
commands[i]->undo();
}
}
};
实现命令模式的客户
class RemotrControl {
private:
Command** onCommands;
Command** offCommands;
std::vector<Command*> undoCommand;
public:
RemotrControl() {
onCommands = new Command*[7];
offCommands = new Command*[7];
Command* noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand.push_back(noCommand);
}
void setCommand(int slot, Command* onCommand, Command* offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
void onButtonWasPushed(int slot) {
onCommands[slot]->execute();
undoCommand.push_back(onCommands[slot]);
}
void offButtonWasPushed(int slot) {
offCommands[slot]->execute();
undoCommand.push_back(offCommands[slot]);
}
void undoButtonWasPushed() {
if (undoCommand.empty()){
printf("No Command\n");
}
else {
undoCommand.back()->undo();
undoCommand.pop_back();
}
}
};
测试
void main() {
RemotrControl* remotrControl = new RemotrControl();
Light* livingRoomLight = new Light("Living Room");
Light* kitchenLight = new Light("Kitchen");
CeilingFan* ceilingFan = new CeilingFan("Living Room");
GarageDoor* garageDoor = new GarageDoor("");
Stereo* stereo = new Stereo("Living Room");
LightOnCommand* livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand* livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand* kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand* kitchenLightOff = new LightOffCommand(kitchenLight);
GarageDoorUpCommand* garageDoorUp = new GarageDoorUpCommand(garageDoor);
GarageDoorDownCommand* garageDoorDown = new GarageDoorDownCommand(garageDoor);
StereoOnWithCDCommand* StereoOnWithCD = new StereoOnWithCDCommand(stereo);
StereoOffCommand* StereoOff = new StereoOffCommand(stereo);
remotrControl->setCommand(0, livingRoomLightOn, livingRoomLightOff);
remotrControl->setCommand(1, kitchenLightOn, kitchenLightOff);
remotrControl->setCommand(3, StereoOnWithCD, StereoOff);
remotrControl->setCommand(4, garageDoorUp, garageDoorDown);
for (int i = 0; i < 7; i++) {
remotrControl->onButtonWasPushed(i);
remotrControl->offButtonWasPushed(i);
}
printf("undo test------------------------------------------------------\n");
remotrControl->offButtonWasPushed(0);
remotrControl->undoButtonWasPushed();
remotrControl->offButtonWasPushed(0);
remotrControl->onButtonWasPushed(0);
remotrControl->undoButtonWasPushed();
remotrControl->undoButtonWasPushed();
remotrControl->undoButtonWasPushed();
printf("------------------------------------------------------\n");
CeilingFanMediumCommand* ceilingFanMedium = new CeilingFanMediumCommand(ceilingFan);
CeilingFanHighCommand* ceilingFanHigh = new CeilingFanHighCommand(ceilingFan);
CeilingFanOffCommand* ceilingFanOff = new CeilingFanOffCommand(ceilingFan);
remotrControl->setCommand(0, ceilingFanMedium, ceilingFanOff);
remotrControl->setCommand(1, ceilingFanHigh, ceilingFanOff);
remotrControl->onButtonWasPushed(0);
remotrControl->offButtonWasPushed(0);
remotrControl->undoButtonWasPushed();
remotrControl->onButtonWasPushed(1);
remotrControl->undoButtonWasPushed();
remotrControl->undoButtonWasPushed();
remotrControl->undoButtonWasPushed();
printf("MacroCommand test------------------------------------------------------\n");
Command* partyOn[] = { livingRoomLightOn,kitchenLightOn,StereoOnWithCD,garageDoorUp };
Command* partyOff[] = { livingRoomLightOff,kitchenLightOff,StereoOff,garageDoorDown };
MacroCommand* partyOnMacro = new MacroCommand(partyOn, 4);
MacroCommand* partyOffMacro = new MacroCommand(partyOff, 4);
remotrControl->setCommand(0, partyOnMacro, partyOffMacro);
remotrControl->onButtonWasPushed(0);
remotrControl->offButtonWasPushed(0);
remotrControl->undoButtonWasPushed();
remotrControl->undoButtonWasPushed();
}
参考:
1. EricFreeman, FreemanElisabeth, 弗里曼, et al. Head First设计模式[M]. 中国电力出版社, 2007.