设计模式(C++)-行为型模式-命令模式

设计模式(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;
}

四、优缺点总结

优点:

  • 解耦调用者和接收者:调用者不需要知道接收者的具体细节
  • 支持撤销/重做:可以轻松实现撤销和重做功能
  • 支持事务:可以将多个命令组合成一个事务
  • 支持命令队列:可以实现命令的排队、延迟执行
  • 易于扩展:添加命令不需要修改现有代码
  • 支持宏命令:可以组合多个命令

缺点:

  • 类的数量增加:每个命令都需要一个具体的命令类
  • 增加系统复杂性:对于简单操作,可能过于复杂
  • 可能产生大量小对象:每个命令都是一个对象
相关推荐
于小猿Sup41 分钟前
VMware在Ubuntu22.04驱动Livox Mid360s
linux·c++·嵌入式硬件·自动驾驶
小小编程路3 小时前
C++ 多线程与并发
java·jvm·c++
程序leo源4 小时前
Qt窗口详解
开发语言·数据库·c++·qt·青少年编程·c#
zh_xuan5 小时前
解决VS Code 控制台中文乱码
c++·vscode·乱码
郭涤生5 小时前
飞凌 RK3588 开发板同显 / 异显模式切换
c++·rk3588
计算机安禾5 小时前
【c++面向对象编程】第38篇:设计原则(二):里氏替换、接口隔离与依赖倒置
开发语言·c++
code_whiter5 小时前
C++1进阶(继承)
开发语言·c++
智者知已应修善业6 小时前
【51单片机LED闪烁10次数码管显示0-9】2023-12-14
c++·经验分享·笔记·算法·51单片机
智者知已应修善业6 小时前
【51单片机2按键控制1个敞亮LED灯闪烁和熄灭】2023-11-3
c++·经验分享·笔记·算法·51单片机
咩咦7 小时前
C++学习笔记20:日期类比较运算符重载
c++·学习笔记·类和对象·运算符重载·比较运算符·日期类