命令模式——参考《Head First设计模式》

来源:互联网 发布:药智网数据查询 编辑:程序博客网 时间:2024/06/17 18:14

命令模式,利用命令对象,把请求封装成一个特定对象,请求者并不需要知道工作的内容是什么,只要有个命令对象能和最终做工作的对象沟通,把事情做完就可以了,这就实现了请求者和动作执行者的解耦,当出现新的动作执行者,只需要为其配套一个命令对象即可,而不需要改动请求者的代码。

以家务遥控器为例,家务遥控器上有许多开关按钮:包括电灯的开关,垃圾桶的开关......并且如果遥控器设计的灵活,应该要达到家用电器插槽式的设计,比如在开关一上如果插入电灯,那么开关就控制着电灯的开关,在开关一上如果插入垃圾桶,那么开关就控制着垃圾桶盖的升降......并且电灯的开关是on(), off()方法,而垃圾桶的开关是up(), down()方法,这样遥控器上的每一组开关按钮都要进行判断,如果插槽中是电灯,那么开关的开就要调用电灯的on(),如果插槽中是垃圾桶,那么开关的关就要调用垃圾桶的up()。这样的设计代码非常复杂,因为你要为遥控器上的每一个按钮进行所有家用电器的判断,并且当加入新的用电器时,要大范围改动家务遥控器的代码,为其每一个开关增加一条判断。这维护起来很麻烦。

如果采用命令模式就可以得到干净的有弹性的设计,在家务遥控器类中加入命令类的数组(命令类是抽象的),这样就可以在运行过程中动态地实例化具体的命令,每个具体的命令均继承自抽象命令类,每个命令类中都有统一的excute()方法,然后将具体的动作委托到家用电器,并且在实例化具体命令类的时候指定固定的编号,将命令对象存入编号下的数组元素中,这样在按下遥控器的某个开关时会调用固定编号的命令对象,进而开启对应的家用电器。

命令模式的代码如下:

#include <iostream>using namespace std;class Command{public:virtual void execute(){}virtual void undo(){}};class NoCommand : public Command{public:void execute(){}void undo(){}};class MacroCommand : public Command{private:Command **commands;public:MacroCommand(Command **commands){this->commands = commands;}void execute(){for(int i=0; i<2; i++){commands[i]->execute();}}void undo(){for(int i=1; i>=0; i--){commands[i]->undo();}}};class Light{public:void on(){cout<<"Light on"<<endl;}void off(){cout<<"Light off"<<endl;}};class LightOnCommand : public Command{private:Light *light;public:LightOnCommand(Light *light){this->light = light;}void execute(){light->on();}void undo(){light->off();}};class LightOffCommand : public Command{private:Light *light;public:LightOffCommand(Light *light){this->light = light;}void execute(){light->off();}void undo(){light->on();}};class GrageDoor{public:void up(){cout<<"GrageDoor up"<<endl;}void down(){cout<<"GrageDoor down"<<endl;}};class GrageDoorOpenCommand : public Command{private:GrageDoor *gragedoor;public:GrageDoorOpenCommand(GrageDoor *gragedoor){this->gragedoor = gragedoor;}void execute(){gragedoor->up();}void undo(){gragedoor->down();}};class GrageDoorCloseCommand : public Command{private:GrageDoor *gragedoor;public:GrageDoorCloseCommand(GrageDoor *gragedoor){this->gragedoor = gragedoor;}void execute(){gragedoor->down();}void undo(){gragedoor->up();}};class RemoteControl{private:Command **onCommands;Command **offCommands;Command *undoCommand;public:RemoteControl(){onCommands = new Command*[7];offCommands = new Command*[7];for(int i=0; i<7; i++){onCommands[i] = new NoCommand();offCommands[i] = new NoCommand();}undoCommand = new NoCommand();}~RemoteControl(){for(int i=0; i<7; i++){if(NULL!=onCommands){delete[] onCommands;onCommands = NULL;}if(NULL!=offCommands){delete[] offCommands;offCommands = NULL;}}if(NULL!=undoCommand){delete undoCommand;undoCommand = NULL;}}void setCommand(int slot, Command *onCommand, Command *offCommand){onCommands[slot] = onCommand;offCommands[slot] = offCommand;}void onButtonWasPushed(int slot){onCommands[slot]->execute();undoCommand = onCommands[slot];}void offButtonWasPushed(int slot){offCommands[slot]->execute();undoCommand = offCommands[slot];}void undoButtonWasPushed(){undoCommand->undo();}};int main(){RemoteControl remotecontrol;Light *livingRoomLight = new Light();GrageDoor *gargeDoor = new GrageDoor();Command *livingRoomLightOn = new LightOnCommand(livingRoomLight);Command *livingRoomLightOff = new LightOffCommand(livingRoomLight);Command *gragedooropen = new GrageDoorOpenCommand(gargeDoor);Command *gargedoorclose = new GrageDoorCloseCommand(gargeDoor);//remotecontrol.setCommand(0,livingRoomLightOn,livingRoomLightOff);//remotecontrol.setCommand(1,gragedooropen,gargedoorclose);//remotecontrol.onButtonWasPushed(0);//remotecontrol.offButtonWasPushed(0);//remotecontrol.onButtonWasPushed(1);//remotecontrol.offButtonWasPushed(1);//remotecontrol.undoButtonWasPushed();Command *patryOn[2] = {livingRoomLightOn,gragedooropen};Command *patryOff[2] = {livingRoomLightOff,gargedoorclose};Command *partyOnMacro = new MacroCommand(patryOn);Command *partyOffMacro = new MacroCommand(patryOff);remotecontrol.setCommand(2,partyOnMacro,partyOffMacro);remotecontrol.onButtonWasPushed(2);remotecontrol.offButtonWasPushed(2);remotecontrol.undoButtonWasPushed();}

命令模式还可以实现动作的撤销,具体做法是在每个具体的命令类中都加入undo方法,比如在打开电灯的命令中undo方法调用电灯的off()方法。

class LightOnCommand : public Command{private:Light *light;public:LightOnCommand(Light *light){this->light = light;}void execute(){light->on();}void undo(){light->off();}};

然后在遥控器类中加入一个undoCommand对象,在onButtonWasPushed方法中将本次按钮按下所调用的命令对象赋给undoCommand对象,这样每次undoCommand对象都会记录最新执行的命令,当按下撤销按钮的时候,调用undoCommand对象的undo()方法即可实现动作的撤销。

void onButtonWasPushed(int slot){onCommands[slot]->execute();undoCommand = onCommands[slot];}void offButtonWasPushed(int slot){offCommands[slot]->execute();undoCommand = offCommands[slot];}void undoButtonWasPushed(){undoCommand->undo();}

命令模式还可以实现宏调用的效果,即一同开启多个家用电器,具体做法是设计MacroCommand类,然后类中以一个Command数组来存储要动作的命令对象,在excute中执行数组中每个对象的excute方法,即可实现批量的命令。

class MacroCommand : public Command{private:Command **commands;public:MacroCommand(Command **commands){this->commands = commands;}void execute(){for(int i=0; i<2; i++){commands[i]->execute();}}void undo(){for(int i=1; i>=0; i--){commands[i]->undo();}}};


0 0
原创粉丝点击