命令模式用于将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
简单来讲,命令模式可以将不同的请求封装成统一的访问方式,比如有些请求函数名是 request1()、有些请求函数名是 request2(),我们通过命令方式可以将其请求函数名统一改成 request,这样便于将多个不同的请求放在同一个容器中,统一遍历访问请求队列即可。
首先,我们先定义命令的接口类,该类用于声明用于同一执行的命令接口叫什么名字,接口类不做任何具体的操作实现。
class Command { public: virtual void excute() = 0; virtual void undo() = 0; };
接下来,我们定义一个类,该类封装了多个不同的操作。如下 Receiver 类定义了四种针对该对象的运算操作。
class Receiver { public: void add(int num) { this->m_number += num; } void minus(int num) { this->m_number -= num; } void multiply(int num) { this->m_number *= num; } void divide(int num) { this->m_number /= num; } public: int m_number {0}; };
我们知道用户可能会针对该对象执行多个不同的操作,为了能够将这些操作封装到同一个容器中,我们可以实现多个具体的命令类,该类需要继承 Command 接口类,如下代码所示:
class AddCommand : public Command { public: AddCommand(Receiver *receiver) : p_receiver(receiver){} virtual void excute() { p_receiver->add(100); } virtual void undo() { p_receiver->minus(100); } private: Receiver *p_receiver; }; class MinusCommand : public Command { public: MinusCommand(Receiver* receiver) : p_receiver(receiver) {} virtual void excute() { p_receiver->minus(20); } virtual void undo() { p_receiver->add(20); } private: Receiver* p_receiver; }; class MultiplyCommand : public Command { public: MultiplyCommand(Receiver* receiver) : p_receiver(receiver) {} virtual void excute() { p_receiver->multiply(3); } virtual void undo() { p_receiver->divide(3); } private: Receiver* p_receiver; }; class DivideCommand : public Command { public: DivideCommand(Receiver* receiver) : p_receiver(receiver) {} virtual void excute() { p_receiver->divide(2); } virtual void undo() { p_receiver->multiply(2); } private: Receiver* p_receiver; };
此刻,用户针对 receiver 对象进行了加减乘除四种操作,我们实例化 4 种操作命令,并将其存储到 vector 容器中,方便简单撤消操作的实现。
#include <iostream> #include <vector> int main() { // 操作对象 Receiver receiver; // 操作命令 Command *comman1 = new AddCommand(&receiver); Command *comman2 = new MinusCommand(&receiver); Command *comman3 = new MultiplyCommand(&receiver); Command *comman4 = new DivideCommand(&receiver); // 存储请求 std::vector<Command*> command_list; command_list.push_back(comman1); command_list.push_back(comman2); command_list.push_back(comman3); command_list.push_back(comman4); // 执行操作 for (Command *command : command_list) { command->excute(); } std::cout << receiver.m_number << std::endl; // 120 // 撤销操作 for (auto it = command_list.rbegin(); it != command_list.rend(); ++it) { (*it)->undo(); } std::cout << receiver.m_number << std::endl; // 0 return 0; }
从上面的实现过程,我们可以看到命令模式中就是将不同操作接口、或者不同的请求进行封装以使得执行接口统一,便于统一的执行。假设只为了实现该功能,我们也可以在设计类时,将 Receiver 类重新设计也可以实现。
如果不知道是否用设计模式,那就不要用,它带来便利的同时,也会使得代码实现变得复杂。