设计模式(C++)-行为型模式-命令模式
一、命令模式概述
命令模式(command pattern):是一种行为型模式,它将请求封装为对象,从而允许你参数化客户端对象,将请求排队,记录请求日志,以及支持可撤销的操作。
核心思想:“将请求封装为对象”,使请求的发送者和接收者解耦。发送者只需要知道如何发送命令,不需要知道命令如何执行或由谁执行。
在C/C++中实现命令模式,你可以遵循以下步骤:
定义命令接口:一个接口,定义了一个执行操作的方法,通常叫做 execute。
实现具体命令:创建实现了命令接口的具体类,这些类包含了对特定操作的调用。
定义客户端:客户端是一个创建和配置命令对象的对象,然后将其传递给请求者。
请求者:请求者是一个知道如何去执行命令的对象,它通常持有一个命令对象的引用或指针。
调用对象:这是实际执行操作的对象,它可能由具体命令内部使用。
可选 - 引入撤销机制:如果需要,可以为命令添加撤销操作
二、命令模式UML类图
命令模式场景
假如现在有一个游戏服务器,该游戏服务器一共可以处理四种不同的请求:处理增加金币、处理增加钻石、处理玩家装备、玩家升级请求。我们需要把这些请求封装成对象,从而加入请求队列一次进行处理。
三、代码实现
//command.h#pragmaonce/* 命令模式(Command Pattern) 是一种行为设计模式,它将一个请求封装为一个对象,从而允许用户使用不同的请求、队列或日志请求来参数化其他对象。 命令模式也支持可撤销的操作。它通常用于将调用操作的对象与知道如何实现该操作的对象解耦。 在C/C++中实现命令模式,你可以遵循以下步骤: 定义命令接口:一个接口,定义了一个执行操作的方法,通常叫做 execute。 实现具体命令:创建实现了命令接口的具体类,这些类包含了对特定操作的调用。 定义客户端:客户端是一个创建和配置命令对象的对象,然后将其传递给请求者。 请求者:请求者是一个知道如何去执行命令的对象,它通常持有一个命令对象的引用或指针。 调用对象:这是实际执行操作的对象,它可能由具体命令内部使用。 可选 - 引入撤销机制:如果需要,可以为命令添加撤销操作 例子: 假如现在有一个游戏服务器,该游戏服务器一共可以处理四种不同的请求:处理增加金币、处理增加钻石、处理玩家装备、玩家升级请求。 我们需要把这些请求封装成对象,从而加入请求队列一次进行处理。 */#include<iostream>#include<queue>#include<memory>usingnamespacestd;classRequest{public://处理增加金币voidAddMoney(){cout<<"给玩家增加金币"<<endl;}//处理增加钻石voidAddDiamond(){cout<<"给玩家增加钻石"<<endl;}//处理玩家装备voidAddEquipment(){cout<<"给玩家穿装备"<<endl;}//玩家升级voidAddLevel(){cout<<"给玩家升级"<<endl;}};//定义命令接口类classAbstractCommand{public:virtualvoidexcute()=0;};//下面是把每一个请求封装为一个请求对象//处理增加金币请求classAddMoneyCommand:publicAbstractCommand{public:AddMoneyCommand(std::shared_ptr<Request>request):m_request(std::move(request)){};virtualvoidexcute();private:std::shared_ptr<Request>m_request;};//处理增加组钻石请求classAddDiamondCommand:publicAbstractCommand{public:AddDiamondCommand(std::shared_ptr<Request>request):m_request(std::move(request)){};virtualvoidexcute();private:std::shared_ptr<Request>m_request;};//处理增加玩家装备请求classAddEquitmentCommand:publicAbstractCommand{public:AddEquitmentCommand(std::shared_ptr<Request>request):m_request(std::move(request)){};virtualvoidexcute();private:std::shared_ptr<Request>m_request;};//处理玩家升级请求classAddLevelCommand:publicAbstractCommand{public:AddLevelCommand(std::shared_ptr<Request>request):m_request(std::move(request)){};virtualvoidexcute();private:std::shared_ptr<Request>m_request;};//服务器程序(命令调用类)classServer{public://将请求对象放入处理队列voidaddRequest(std::shared_ptr<AbstractCommand>command);//启动处理程序voidstartExcute();private:queue<std::shared_ptr<AbstractCommand>>m_commands;};voidtestCommand();//command.cc#include"command.h"#include<thread>voidAddMoneyCommand::excute(){m_request->AddMoney();}voidAddDiamondCommand::excute(){m_request->AddDiamond();}voidAddEquitmentCommand::excute(){m_request->AddEquipment();}voidAddLevelCommand::excute(){m_request->AddLevel();}//将请求对象放入处理队列voidServer::addRequest(std::shared_ptr<AbstractCommand>command){m_commands.push(command);}//启动处理程序voidServer::startExcute(){while(!m_commands.empty()){std::shared_ptr<AbstractCommand>command=std::move(m_commands.front());std::this_thread::sleep_for(std::chrono::seconds(2));command->excute();m_commands.pop();}}voidtestCommand(){cout<<"=================command start==============="<<endl;std::shared_ptr<Request>request=std::make_shared<Request>();//客户端增加金币的请求std::shared_ptr<AddMoneyCommand>addmoney=std::make_shared<AddMoneyCommand>(std::move(request));//客户端增加钻石的请求std::shared_ptr<AddDiamondCommand>addiamond=std::make_shared<AddDiamondCommand>(std::move(request));//客户端增加装备的请求std::shared_ptr<AddEquitmentCommand>addquitment=std::make_shared<AddEquitmentCommand>(std::move(request));//客户端升级的请求std::shared_ptr<AddLevelCommand>addlevel=std::make_shared<AddLevelCommand>(std::move(request));//将客户端的请求加入到请求队列中std::shared_ptr<Server>server=std::make_shared<Server>();server->addRequest(std::move(addmoney));server->addRequest(std::move(addiamond));server->addRequest(std::move(addquitment));server->addRequest(std::move(addlevel));//服务器开始处理请求server->startExcute();cout<<"=================command end==============="<<endl;}四、优缺点总结
优点:
- 解耦调用者和接收者:调用者不需要知道接收者的具体细节
- 支持撤销/重做:可以轻松实现撤销和重做功能
- 支持事务:可以将多个命令组合成一个事务
- 支持命令队列:可以实现命令的排队、延迟执行
- 易于扩展:添加命令不需要修改现有代码
- 支持宏命令:可以组合多个命令
缺点:
- 类的数量增加:每个命令都需要一个具体的命令类
- 增加系统复杂性:对于简单操作,可能过于复杂
- 可能产生大量小对象:每个命令都是一个对象