命令模式是指将用户的请求封装成(命令)对象,从而可将用户不同的请求进行参数化、对这些请求排序或记录请求日志、以及支持回滚恢复操作。记得以前刚开始使用Photoshop时,就发现它的操作历史记录面板特别好用,现在想想,其实也可以通过命令模式来设计。命令模式主要是将请求抽象成对象,以此作为中间的一个中转站,转达请求命令,从而将请求方与响应方解耦。在逻辑层面上,用户只需要发布自己的命令即可,至于由什么样的响应者来处理,则需要看具体下达的是什么样的命令。模式类结构图参考如下:
模式编码结构参考如下:
1 namespace command 2 { 3 class Receiver 4 { 5 public: 6 void doAction() {/*some code here........*/} 7 8 }; 9 10 class ICommand 11 { 12 public: 13 virtual void action() = 0; 14 // some code here........ 15 16 }; 17 18 class ConcreteCommand : public ICommand 19 { 20 public: 21 virtual void action() { 22 // some code here........ 23 _receiver->doAction(); 24 } 25 26 private: 27 Receiver* _receiver; 28 29 };//class ConcreteCommand 30 31 class Invoker 32 { 33 public: 34 void call() { 35 // some code here........ 36 // this is a test case below. 37 auto pCommand = new (std::nothrow) ConcreteCommand(); 38 pCommand->action(); 39 } 40 41 }; 42 43 }//namespace command
既然命令对象的职责相当于是一个中转站转达请求,则命令模式重点在于命令对象的设计。试想下,如果一个请求需要被多个响应者响应,则只需要简简单单设计一个“复合命令”对象即可。参考如下:
1 class MacroCommand 2 { 3 public: 4 // some code here........ 5 virtual void execute() override { 6 // call execute action of all children command object 7 // some code here........ 8 } 9 10 private: 11 typedef std::list<ICommand*> TCmdList; 12 13 TCmdList m_listCmds; 14 15 };//class MacroCommand
对于命令对象的设计,最复杂的情况是直接将响应者的逻辑直接在命令对象中封装起来,此时就相当于没有使用命令模式的情形,这是一种完全退化的情况。最简单的情况是将请求转抛给具体的响应者,此时命令对象就仅仅只是一个桥梁,起到的作用是将请求方与响应方解耦合。因此,关于命令对象的封装会有个度的评估,此需要看具体设计、项目实际需要、以及当时的上下文环境而斟酌。