c/c++设计模式----命令模式
1. 通过一个范例引出命令模式代码编写方法
//红烧鱼,锅包肉
#include <iostream> #include <list> #ifdef _DEBUG //只在Debug(调试)模式下 #ifndef DEBUG_NEW #define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符 #define new DEBUG_NEW #endif #endif //#include <boost/type_index.hpp> using namespace std; //#pragma warning(disable : 4996) namespace _nmsp1 { //厨师类 class Cook { public: //做红烧鱼 void cook_fish() { cout << "做一盘红烧鱼菜品" << endl; } //做锅包肉 void cook_meat() { cout << "做一盘锅包肉菜品" << endl; } //做其他各种菜品......略 }; //---------------------- //厨师做的每样菜品对应的抽象类 class Command { public: Command(Cook* pcook) { m_pcook = pcook; } //做父类时析构函数为虚函数 virtual ~Command() { if (m_pcook != nullptr) { delete m_pcook; m_pcook = nullptr; } } virtual void Execute() = 0; //virtual void Undo() = 0; protected: Cook* m_pcook; //子类需要访问 }; //做红烧鱼菜品命令 class CommandFish :public Command { public: CommandFish(Cook* pcook) :Command(pcook) {} virtual void Execute() { m_pcook->cook_fish(); } }; //做锅包肉菜品命令 class CommandMeat :public Command { public: CommandMeat(Cook* pcook) :Command(pcook) {} virtual void Execute() { m_pcook->cook_meat(); //将一个动作封装成了一个对象 } }; //----------------- /* //服务员类 class Waiter { public: void SetCommand(Command* pcommand) //顾客把便签交给服务员 { m_pcommand = pcommand; } void Notify() //服务器员将便签交到厨师手里让厨师开始做菜 { m_pcommand->Execute(); } private: Command* m_pcommand; //服务员手中握着顾客书写的菜品便签 };*/ //服务员类 class Waiter { public: //将顾客的便签增加到便签列表中,即便一个便签中包含多个菜品,这也相当于一道一道菜品加入到列表中 void AddCommand(Command* pcommand) { m_commlist.push_back(pcommand); } //如果顾客想撤单则将便签从列表中删除 void DelCommand(Command* pcommand) { m_commlist.remove(pcommand); } void Notify() //服务员将所有便签一次性交到厨师手里让厨师开始按顺序做菜 { //依次让厨师做每一道菜品 for (auto iter = m_commlist.begin(); iter != m_commlist.end(); ++iter) { (*iter)->Execute(); } } private: //一个便签中可以包含多个菜品甚至可以一次收集多个顾客的便签,达到一次性通知厨师做多道菜的效果 std::list<Command*> m_commlist; //菜品列表,每道菜品作为一项,如果一个便签中有多个菜品,则这里将包含多项 }; //-------------------- //实习服务器员类 class Traineewaiter { public: Traineewaiter(Command* pcommand) :m_pcommand(pcommand) {} //构造函数 void Notify() //实习服务员将便签交到厨师手里让厨师开始做菜 { m_pcommand->Execute(); } ~Traineewaiter() //析构函数 { if (m_pcommand != nullptr) { delete m_pcommand; m_pcommand = nullptr; } } private: Command* m_pcommand; //实习服务员手中握着顾客书写的菜品便签 }; //------------------------- //宏命令:把多个命令组合 class CommandMacro :public Command { public: void AddCommand(Command* pcommand) { m_commlist.push_back(pcommand); } virtual void Execute() { for (auto iter = m_commlist.begin(); iter != m_commlist.end(); ++iter) { (*iter)->Execute(); } } private: std::list<Command*> m_commlist; }; } class TC { public: void operator()(int tv) { cout << "TC::Operator(int tv)执行了,tv=" << tv << endl; } int operator()(int tv1, int tv2) { cout << "TC::Operator(int tv1, int tv2)执行了,tv1=" << tv1 << "tv2=" << tv2 << endl; return 1; } }; template <class T> //T代表可调用对象的类型 void functmptest(T callobj) //callobj是个可调用对象 { callobj(100); return; } int main() { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口 /* _nmsp1::Cook* pcook = new _nmsp1::Cook(); pcook->cook_fish(); pcook->cook_meat(); //释放资源 delete pcook; */ /* _nmsp1::Cook cook; _nmsp1::Command* pcmd1 = new _nmsp1::CommandFish(&cook); pcmd1->Execute(); //做红烧鱼 _nmsp1::Command* pcmd2 = new _nmsp1::CommandMeat(&cook); pcmd2->Execute(); //做锅包肉 //释放资源 delete pcmd1; delete pcmd2; */ /* _nmsp1::Cook cook; _nmsp1::Waiter *pwaiter = new _nmsp1::Waiter(); _nmsp1::Command* pcmd1 = new _nmsp1::CommandFish(&cook); pwaiter->SetCommand(pcmd1); pwaiter->Notify(); //做红烧鱼 _nmsp1::Command* pcmd2 = new _nmsp1::CommandMeat(&cook); pwaiter->SetCommand(pcmd2); pwaiter->Notify(); //做锅包肉 //释放资源 delete pcmd1; delete pcmd2; delete pwaiter; */ /* //_nmsp1::Cook cook; //一次性在便签上写下多道菜品 //_nmsp1::Command* pcmd1 = new _nmsp1::CommandFish(&cook); //_nmsp1::Command* pcmd2 = new _nmsp1::CommandMeat(&cook); _nmsp1::Command* pcmd1 = new _nmsp1::CommandFish(new _nmsp1::Cook()); _nmsp1::Command* pcmd2 = new _nmsp1::CommandMeat(new _nmsp1::Cook()); _nmsp1::Waiter *pwaiter= new _nmsp1::Waiter(); //把多道菜品分别加入到菜品列表 pwaiter->AddCommand(pcmd1); pwaiter->AddCommand(pcmd2); //服务员一次性通知厨师做多道菜 pwaiter->Notify(); //释放资源 delete pcmd1; delete pcmd2; delete pwaiter; */ _nmsp1::Traineewaiter* pwaitersx1 = new _nmsp1::Traineewaiter(new _nmsp1::CommandFish(new _nmsp1::Cook())); pwaitersx1->Notify(); //做红烧鱼 _nmsp1::Traineewaiter* pwaitersx2 = new _nmsp1::Traineewaiter(new _nmsp1::CommandMeat(new _nmsp1::Cook())); pwaitersx2->Notify(); //做锅包肉 delete pwaitersx1; delete pwaitersx2; TC tc; tc(20); //调用的是()操作符,这就是可调用对象。等价于tc.operator()(20); tc(30,50); functmptest(tc); return 0; }
命令模式的UML:
引入命令(Command)模式:五种角色
//a)Receiver(接收者类):Cook类,cook_fish,cook_meat;
//b)Invoker(调用者类):Waiter类。
//c)Command(抽象命令类):Command类。
//d)ConcreteCommand(具体命令类):CommandFish类和CommandMeat类。
//e)Client(客户端)
//定义:将一个请求或命令封装为一个对象,l以便这些请求可以以对象的方式通过参数进行传递,对象化了的请求还可以排队执行或者
//根据需要将这些请求录入日志供查看和排错,以及支持请求执行后的可撤销操作。
//能力:对请求进行封装,命令对象将动作和接收者包裹到了对象中并只暴露了一个Execute方法让接收者执行动作。
//(3)命令模式用途研究:异步执行、延迟执行、排队执行、撤销、执行过程中增加日志记录等,是命令模式主要应用场景
//(3.1)改造范例增加对象使用时的独立性
//(3.2)命令模式使用场景谈与特点总结
//a)在一些绘图软件中
//b)遥控器实现对控制设备的解耦
//c)任务的定期调度执行:Task Scheduler
//d)游戏中时光倒流系统和回放系统的实现
//命令模式的特点:
//a)
//b)
//c)
//问题思考:
//a)命令对象作为回调函数的替代:Command模式是回调机制的一个面向对象的替代品。
//b)极端情形:不引入调用者类;Command子类自己实现相关功能不引入接收者。
//c)命令模式中命令对象与现代C++中可调用对象的比较
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,并且能够支持可撤销的操作。命令模式主要包含四个基本角色:
- 命令接口(Command):定义了执行命令的方法(通常是一个简单的
execute
方法)。 - 具体命令类(Concrete Command):实现了命令接口,执行实际的操作。这些类通常会持有接收者对象的引用。
- 接收者(Receiver):执行命令实际所需的操作。接收者是业务逻辑的实际承担者。
- 调用者(Invoker):请求命令执行。这类对象持有命令对象并在某个时刻通过命令接口来调用命令对象。
让我们通过一个简单的示例来解释命令模式:
假设我们有一个简单的家电控制系统,其中包括一个灯(Light)和一个遥控器(Remote Control)。我们希望使用命令模式来实现打开和关闭灯的功能。
假设我们还是要控制灯(Light),我们希望能够开灯、关灯以及撤销上一步操作。
1. 命令接口
首先,我们定义一个命令接口 Command
:
#include <iostream>
#include <stack>
// Command 接口
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
这里,我们添加了一个 undo
方法,以支持撤销功能。
2. 接收者类
接下来是我们的接收者类 Light
:
class Light {
public:
void on() {
std::cout << "The light is on." << std::endl;
}
void off() {
std::cout << "The light is off." << std::endl;
}
};
3. 具体命令类
接下来我们定义具体的命令类 LightOnCommand
和 LightOffCommand
:
// 开灯命令
class LightOnCommand : public Command {
private:
Light* light;
public:
LightOnCommand(Light* light) : light(light) {}
void execute() override {
light->on();
}
void undo() override {
light->off();
}
};
// 关灯命令
class LightOffCommand : public Command {
private:
Light* light;
public:
LightOffCommand(Light* light) : light(light) {}
void execute() override {
light->off();
}
void undo() override {
light->on();
}
};
每个具体命令类都持有一个 Light
对象,并实现了 execute
和 undo
方法。
4. 调用者类
我们需要一个调用者类 RemoteControl
,它将负责发送命令:
cppCopy Code
class RemoteControl {
private:
Command* command;
std::stack<Command*> history; // 用于存储历史命令以支持撤销
public:
void setCommand(Command* cmd) {
command = cmd;
}
void pressButton() {
command->execute();
history.push(command);
}
void pressUndo() {
if (!history.empty()) {
Command* lastCommand = history.top();
lastCommand->undo();
history.pop();
}
}
};
这里,我们添加了一个 history
栈来记录执行过的命令,从而可以实现撤销功能。
5. 使用示例
最后,我们编写 main
函数来演示整个流程:
int main() {
Light livingRoomLight;
LightOnCommand livingRoomLightOn(&livingRoomLight);
LightOffCommand livingRoomLightOff(&livingRoomLight);
RemoteControl remote;
// 开灯
remote.setCommand(&livingRoomLightOn);
remote.pressButton();
// 关灯
remote.setCommand(&livingRoomLightOff);
remote.pressButton();
// 撤销关灯
remote.pressUndo();
return 0;
}
#include <iostream> #include <stack> // Command 接口 class Command { public: virtual ~Command() {} virtual void execute() = 0; virtual void undo() = 0; }; // 接收者:灯 class Light { public: void on() { std::cout << "The light is on." << std::endl; } void off() { std::cout << "The light is off." << std::endl; } }; // 具体命令类:开灯命令 class LightOnCommand : public Command { private: Light* light; public: LightOnCommand(Light* light) : light(light) {} void execute() override { light->on(); } void undo() override { light->off(); } }; // 具体命令类:关灯命令 class LightOffCommand : public Command { private: Light* light; public: LightOffCommand(Light* light) : light(light) {} void execute() override { light->off(); } void undo() override { light->on(); } }; // 调用者:遥控器 class RemoteControl { private: Command* command; std::stack<Command*> history; // 用于存储历史命令以支持撤销 public: void setCommand(Command* cmd) { command = cmd; } void pressButton() { command->execute(); history.push(command); } void pressUndo() { if (!history.empty()) { Command* lastCommand = history.top(); lastCommand->undo(); history.pop(); } } }; int main() { Light livingRoomLight; LightOnCommand livingRoomLightOn(&livingRoomLight); LightOffCommand livingRoomLightOff(&livingRoomLight); RemoteControl remote; // 开灯 remote.setCommand(&livingRoomLightOn); remote.pressButton(); // 关灯 remote.setCommand(&livingRoomLightOff); remote.pressButton(); // 撤销关灯 remote.pressUndo(); return 0; }
解释
- 命令接口 定义了
execute
和undo
方法。 - 具体命令类 实现了命令接口,并在
execute
中调用Light
的相应方法,在undo
中调用相反的方法。 - 接收者 是
Light
类,它包含了具体的操作方法(on
和off
)。 - 调用者 是
RemoteControl
类,它持有命令对象并调用其方法。它还维护一个历史栈以支持撤销功能。
通过这种方式,命令模式解耦了命令的发送者和接收者,使得请求可以被参数化、排队、记录或撤销。