设计模式(C++)-行为型模式-命令模式
一、命令模式概述
命令模式(command pattern):是一种行为型模式,它将请求封装为对象,从而允许你参数化客户端对象,将请求排队,记录请求日志,以及支持可撤销的操作。
核心思想 :"将请求封装为对象",使请求的发送者和接收者解耦。发送者只需要知道如何发送命令,不需要知道命令如何执行或由谁执行。
在C/C++中实现命令模式,你可以遵循以下步骤:
定义命令接口:一个接口,定义了一个执行操作的方法,通常叫做 execute。
实现具体命令:创建实现了命令接口的具体类,这些类包含了对特定操作的调用。
定义客户端:客户端是一个创建和配置命令对象的对象,然后将其传递给请求者。
请求者:请求者是一个知道如何去执行命令的对象,它通常持有一个命令对象的引用或指针。
调用对象:这是实际执行操作的对象,它可能由具体命令内部使用。
可选 - 引入撤销机制:如果需要,可以为命令添加撤销操作
二、命令模式UML类图
命令模式场景
假如现在有一个游戏服务器,该游戏服务器一共可以处理四种不同的请求:处理增加金币、处理增加钻石、处理玩家装备、玩家升级请求。我们需要把这些请求封装成对象,从而加入请求队列一次进行处理。

三、代码实现
cpp
//command.h
#pragma once
/*
命令模式(Command Pattern)
是一种行为设计模式,它将一个请求封装为一个对象,从而允许用户使用不同的请求、队列或日志请求来参数化其他对象。
命令模式也支持可撤销的操作。它通常用于将调用操作的对象与知道如何实现该操作的对象解耦。
在C/C++中实现命令模式,你可以遵循以下步骤:
定义命令接口:一个接口,定义了一个执行操作的方法,通常叫做 execute。
实现具体命令:创建实现了命令接口的具体类,这些类包含了对特定操作的调用。
定义客户端:客户端是一个创建和配置命令对象的对象,然后将其传递给请求者。
请求者:请求者是一个知道如何去执行命令的对象,它通常持有一个命令对象的引用或指针。
调用对象:这是实际执行操作的对象,它可能由具体命令内部使用。
可选 - 引入撤销机制:如果需要,可以为命令添加撤销操作
例子:
假如现在有一个游戏服务器,该游戏服务器一共可以处理四种不同的请求:处理增加金币、处理增加钻石、处理玩家装备、玩家升级请求。
我们需要把这些请求封装成对象,从而加入请求队列一次进行处理。
*/
#include <iostream>
#include <queue>
#include <memory>
using namespace std;
class Request{
public:
//处理增加金币
void AddMoney()
{
cout << "给玩家增加金币" << endl;
}
//处理增加钻石
void AddDiamond()
{
cout << "给玩家增加钻石" << endl;
}
//处理玩家装备
void AddEquipment()
{
cout << "给玩家穿装备" << endl;
}
//玩家升级
void AddLevel()
{
cout << "给玩家升级" << endl;
}
};
//定义命令接口类
class AbstractCommand {
public:
virtual void excute()=0;
};
//下面是把每一个请求封装为一个请求对象
//处理增加金币请求
class AddMoneyCommand :public AbstractCommand {
public:
AddMoneyCommand(std::shared_ptr<Request> request) :m_request(std::move(request)) {};
virtual void excute();
private:
std::shared_ptr<Request> m_request;
};
//处理增加组钻石请求
class AddDiamondCommand :public AbstractCommand {
public:
AddDiamondCommand(std::shared_ptr<Request> request) :m_request(std::move(request)) {};
virtual void excute();
private:
std::shared_ptr<Request> m_request;
};
//处理增加玩家装备请求
class AddEquitmentCommand :public AbstractCommand {
public:
AddEquitmentCommand(std::shared_ptr<Request> request) :m_request(std::move(request)) {};
virtual void excute();
private:
std::shared_ptr<Request> m_request;
};
//处理玩家升级请求
class AddLevelCommand :public AbstractCommand {
public:
AddLevelCommand(std::shared_ptr<Request> request) :m_request(std::move(request)) {};
virtual void excute();
private:
std::shared_ptr<Request> m_request;
};
//服务器程序(命令调用类)
class Server {
public:
//将请求对象放入处理队列
void addRequest(std::shared_ptr<AbstractCommand>command);
//启动处理程序
void startExcute();
private:
queue<std::shared_ptr<AbstractCommand> > m_commands;
};
void testCommand();
//command.cc
#include "command.h"
#include <thread>
void AddMoneyCommand::excute() {
m_request->AddMoney();
}
void AddDiamondCommand::excute() {
m_request->AddDiamond();
}
void AddEquitmentCommand::excute() {
m_request->AddEquipment();
}
void AddLevelCommand::excute() {
m_request->AddLevel();
}
//将请求对象放入处理队列
void Server::addRequest(std::shared_ptr<AbstractCommand>command) {
m_commands.push(command);
}
//启动处理程序
void Server::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();
}
}
void testCommand() {
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;
}
四、优缺点总结
优点:
- 解耦调用者和接收者:调用者不需要知道接收者的具体细节
- 支持撤销/重做:可以轻松实现撤销和重做功能
- 支持事务:可以将多个命令组合成一个事务
- 支持命令队列:可以实现命令的排队、延迟执行
- 易于扩展:添加命令不需要修改现有代码
- 支持宏命令:可以组合多个命令
缺点:
- 类的数量增加:每个命令都需要一个具体的命令类
- 增加系统复杂性:对于简单操作,可能过于复杂
- 可能产生大量小对象:每个命令都是一个对象