命令模式
命令(Command)模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式UML类图
Command:定义命令的接口,声明执行的方法。
ConcreteCommand类:将一个接收者对象绑定于一个动作,调用接受者相应的操作,以实现Excute。
Invoker类,要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口
Receiver类:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
Client:客户端代码,创建一个具体命令对象并设定它的接收者。
命令模式的作用
1.用它比较可以容易的设计一个命令队列
2.在需要的情况下,可以较容易地将命令记入日志
3.允许接收请求一方决定是否要否决请求
4.可以容易的实现对命令的撤销和重做
5.由于加进新的具体命令类不影响其它的类,因此增加新的具体命令类很容易。
6.命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开
注:不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般不要急着去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销、恢复操作等功能时,把原来的代码重构为命令模式才有意义。
代码示例
就用C++实现《大话设计模式》中提供的吃烧烤的这个例子吧,场景是:吃烧烤,角色有服务员、烧烤师傅、顾客,顾客相当于Client,服务员相当于Invoker,他要维护一个命令的列表,还要进行日志记录,支持撤销、重做,烧烤师傅相当于Receiver,顾客向服务员发出的要烤肉、要鸡翅,这些都是具体的命令。
1.Command类需要知道这条命令是谁接收的,所以我们在实际设计的时候要先写Receiver类

#ifndef COMMAND_H_ #define COMMAND_H_ #include "Receiver.h" class Command { protected: Barbecuer m_Receiver; public: Command() = default; //由接收者执行命令 virtual void executeCommand() = 0; virtual ~Command() = default; }; #endif
2.Receiver类是命令的具体执行者

#ifndef RECEIVER_H_ #define RECEIVER_H_ #include <iostream> class Barbecuer { public: //烤羊肉的动作 void BakeMutton() { std::cout << "Bake Mutton." << std::endl; } //烤鸡翅 void BakeChickenWing() { std::cout << "Bake Chicken Wing." << std::endl; } Barbecuer() = default; ~Barbecuer() = default; }; #endif
3.具体的命令类

#ifndef BAKEMUTTONCOMMAND_H_ #define BAKEMUTTONCOMMAND_H_ #include "Command.h" //执行烤羊肉的命令 class BakeMuttonCommand : public Command { public: void executeCommand() override { m_Receiver.BakeMutton(); } BakeMuttonCommand(Barbecuer objReceiver) { m_Receiver=objReceiver; } ~BakeMuttonCommand() = default; }; #endif #ifndef BAKECHICKENWINGCOMMAND_H_ #define BAKECHICKENWINGCOMMAND_H_ //烤鸡翅的具体命令 #include "Command.h" class BakeChickenWingCommand : public Command { public: void executeCommand() override { m_Receiver.BakeChickenWing(); } BakeChickenWingCommand(Barbecuer objBarbecuer) { m_Receiver = objBarbecuer; } ~BakeChickenWingCommand() = default; }; #endif
4.Invoker类

#ifndef INVOKER_H_ #define INVOKER_H_ #include <list> #include "Command.h" #include "BakeChickenWingCommand.h" #include <iostream> #include <typeinfo> class Invoker { private: std::list<Command*> m_listCommand; public: //取消订单 void cancelOrder(Command *objCommand) { m_listCommand.remove(objCommand); std::cout << "Order is Canceled." << std::endl; } //因为店里没有烤鸡翅了,所以Invoker有权拒绝顾客提出的这个订单命令 void setOrder(Command *objCommand) { if(typeid(*objCommand) == typeid(BakeChickenWingCommand)) { std::cout << "No ChickenWing,Please order others." << std::endl; } else { m_listCommand.push_back(objCommand); std::cout << "Increase Order:BakeMutton" << std::endl; //记录日志 } } //通知接收者执行 void notify() { for(auto value : m_listCommand) { value->executeCommand(); } } Invoker() = default; ~Invoker() = default; }; #endif
5.Client

#include "Invoker.h" #include "Receiver.h" #include "BakeMuttonCommand.h" #include "BakeChickenWingCommand.h" #include "Command.h" using namespace std; int main(int argc,char *argv[]) { //开店前的准备 //烧烤店事先就准备和了烤肉师傅和服务员以及菜单,就等着顾客上门了 Barbecuer boy; //烧烤师傅 //菜单 BakeMuttonCommand objBakeMuttonCommand1(boy); Command *bakeMuttonCommand1 = &objBakeMuttonCommand1; BakeMuttonCommand objBakeMuttonCommand2(boy); Command *bakeMuttonCommand2 = &objBakeMuttonCommand2; BakeChickenWingCommand objBakeChickenWingCommand(boy); Command *bakeChickenWingCommand1 = &objBakeChickenWingCommand; //服务员 Invoker girl; //顾客来了,开始营业 girl.setOrder(bakeMuttonCommand1); girl.setOrder(bakeMuttonCommand2); girl.setOrder(bakeChickenWingCommand1); girl.notify(); return (1); }