当前位置: 首页 > news >正文

突破编程_C++_设计模式(命令模式)

1 命令模式的基本概念

C++ 命令模式是一种设计模式,它允许将请求封装为一个对象,从而可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式的主要目的是将请求封装为对象,从而可以使用不同的请求把客户端与接收者解耦。

在命令模式中,通常包含以下几个关键角色:

(1)命令(Command): 这是一个抽象类,它声明了执行操作的接口。具体的命令类会实现这个接口,并绑定到一个接收者对象上。当命令对象被调用时,它会调用接收者的相应操作。

(2)具体命令(ConcreteCommand): 这是命令接口的具体实现类,它持有一个接收者对象,并实现了命令接口中的执行方法。当执行方法被调用时,它会调用接收者的相应方法来完成请求的操作。

(3)请求者(Invoker): 请求者负责调用命令对象的执行方法。它不需要知道具体的命令对象类型,只需要知道它是一个命令对象即可。

(4)接收者(Receiver): 接收者对象知道如何执行请求的操作。它具体实现了请求的功能。

2 命令模式的实现步骤

命令模式的实现步骤如下:

(1)定义命令接口:

  • 创建一个抽象类(或接口),声明一个执行操作的方法。这个方法将是所有具体命令类共有的接口。

(2)实现具体命令类:

  • 创建具体命令类,继承自命令接口。
  • 在具体命令类中,通常包含一个指向接收者对象的指针(或引用)。
  • 实现执行操作的方法,该方法内部调用接收者对象的相应方法来完成请求的操作。

(3)创建接收者类:

  • 定义一个接收者类,它包含实际执行操作的逻辑。
  • 接收者类的方法对应于命令类需要执行的操作。

(4)创建请求者类:

  • 请求者类负责调用命令对象的执行方法。
  • 请求者不需要知道具体的命令对象类型,它只需要持有一个命令对象的指针(或引用)。
  • 请求者类可以存储多个命令对象,并按照需要调用它们的执行方法。

(5)将命令对象与接收者对象关联:

  • 在创建具体命令对象时,将接收者对象传递给命令对象,以便命令对象在执行时可以调用接收者的方法。

(6)使用命令模式:

  • 在客户端代码中,创建接收者对象。
  • 创建与接收者对象关联的具体命令对象。
  • 将命令对象传递给请求者对象。
  • 请求者对象调用命令对象的执行方法,从而间接调用接收者的方法,完成请求的操作。

如下为样例代码:

#include <iostream>  
#include <memory>  
#include <string>  
#include <vector>  // 创建接收者类  
class Receiver {
public:void action() {std::cout << "Receiver::action() called" << std::endl;}
};// 定义命令接口  
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;
};// 实现具体命令类  
class ConcreteCommand : public Command {
public:ConcreteCommand(std::shared_ptr<Receiver> receiver) : m_receiver(receiver) {}void execute() override {m_receiver->action();}
private:std::shared_ptr<Receiver> m_receiver;
};// 创建请求者类  
class Invoker {
public:void setCommand(std::unique_ptr<Command> command) {commands.push_back(std::move(command));}void executeCommands() {for (auto& cmd : commands) {cmd->execute();}}private:std::vector<std::unique_ptr<Command>> commands;
};// 客户端代码  
int main() 
{// 创建接收者对象  std::shared_ptr<Receiver> receiver = std::make_shared<Receiver>();// 创建具体命令对象,并与接收者对象关联  std::unique_ptr<Command> command = std::make_unique<ConcreteCommand>(receiver);// 创建请求者对象,并将命令对象传递给请求者  Invoker invoker;invoker.setCommand(std::move(command));// 请求者调用命令对象的执行方法  invoker.executeCommands(); // 输出: Receiver::action() called  return 0;
}

上面代码的输出为:

Receiver::action() called

在这个示例中:

  • Command 是一个抽象类,定义了命令的接口。
  • ConcreteCommand 是 Command 的具体实现,它持有一个指向 Receiver 的 shared_ptr,并在 execute 方法中调用 Receiver 的 action 方法。
  • Receiver 是接收者类,包含了实际要执行的操作。
  • Invoker 是请求者类,它持有一个 Command 对象的列表,并提供了一个方法来执行这些命令。

在 main 函数中,创建了 Receiver 对象和 ConcreteCommand 对象,并将 Receiver 对象传递给 ConcreteCommand 对象。然后将 ConcreteCommand 对象传递给 Invoker 对象,并调用 Invoker 的 executeCommands 方法来执行命令。最终,Receiver 的 action 方法被调用,输出了相应的信息。

3 命令模式的应用场景

C++命令模式的应用场景广泛,主要适用于以下情况:

(1)当功能需要支持撤销和恢复撤销操作时: 命令模式通过封装命令对象,可以方便地记录和执行命令历史,从而支持撤销和恢复撤销操作。这在图形编辑、文本编辑等应用中非常有用,例如,在绘制图形时,用户可以撤销最近的操作以返回到之前的状态。

(2)当需要设计一组命令,并且命令之间可以相互组合时: 命令模式支持将多个命令组合成一个复合命令,从而可以一次性执行多个操作。这在需要执行一系列相关操作时非常有用,例如,在编辑器中,用户可能希望将多个编辑操作(如剪切、复制、粘贴)组合成一个宏命令,以便一次性执行。

(3)当系统需要将命令发起者和命令执行者解耦时: 命令模式允许将请求封装为命令对象,从而使发送请求的对象和执行请求的对象解耦。这使得发送请求的对象不需要知道请求如何被完成,增加了系统的灵活性和可维护性。例如,在用户界面和后台逻辑之间,命令模式可以确保它们之间的松耦合,使得修改其中一方不会影响到另一方。

(4)当系统需要在不同时间指定、排列和执行请求时: 命令模式允许将命令对象存储在队列或列表中,然后根据需要按顺序执行它们。这对于实现批量操作、延迟执行或任务调度等功能非常有用。

总的来说,C++命令模式适用于需要解耦请求发送者和请求执行者、支持撤销操作、需要组合多个命令或在不同时间执行请求的场景。它能够提高系统的灵活性、可维护性和可扩展性。

3.1 命令模式应用于需要支持撤销和恢复撤销操作的场景

在 C++ 中,命令模式特别适用于需要支持撤销和恢复撤销操作的场景,如下为样例代码:

#include <iostream>  
#include <memory>  
#include <vector>  
#include <stack>  
#include <string>  // 接收者类  
class Receiver {
public:std::string action() {std::string currentState = "New state"; // 假设这是某种状态  // 执行一些操作...  return currentState;}void restore(const std::string& state) {// 恢复到之前的状态...  std::cout << "Restoring to state: " << state << std::endl;}
};// 命令接口  
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;
};// 具体命令类  
class ConcreteCommand : public Command {
public:ConcreteCommand(std::shared_ptr<Receiver> receiver) : receiver(receiver) {}void execute() override {previousState = receiver->action();std::cout << "Command executed: " << previousState << std::endl;}void undo() override {receiver->restore(previousState);std::cout << "Command undone: " << previousState << std::endl;}private:std::shared_ptr<Receiver> receiver;std::string previousState;
};// 撤销管理器  
class UndoManager {
public:void executeCommand(std::shared_ptr<Command> cmd) {cmd->execute();commands.push(cmd);}void undoLastCommand() {if (!commands.empty()) {auto cmd = commands.top();commands.pop();cmd->undo();}else {std::cout << "No commands to undo." << std::endl;}}private:std::stack<std::shared_ptr<Command>> commands;
};int main() 
{// 创建接收者对象  auto receiver = std::make_shared<Receiver>();// 创建命令对象  auto command = std::make_shared<ConcreteCommand>(receiver);// 创建撤销管理器  UndoManager undoManager;// 执行命令  undoManager.executeCommand(command);// 撤销命令  undoManager.undoLastCommand();return 0;
}

上面代码的输出为:

Command executed: New state
Restoring to state: New state
Command undone: New state

在这个示例中:

  • Command 是一个抽象类,定义了命令的接口,包括 execute 和 undo 方法。
  • ConcreteCommand 是 Command 的具体实现,它持有一个指向 Receiver 的 shared_ptr,并在 execute 方法中执行操作,同时保存当前状态以便在 undo 方法中恢复。
  • Receiver 类包含实际执行操作的逻辑,以及一个 restore 方法用于恢复之前的状态。
  • UndoManager 类负责管理命令的撤销操作,它使用一个 stack 来存储执行过的命令对象。当调用 executeCommand 方法时,它会执行命令并将其推入栈中;当调用 undoLastCommand 方法时,它会从栈顶弹出命令并执行其 undo 方法。

在 main 函数中,创建了一个 Receiver 对象和一个 ConcreteCommand 对象,并通过 UndoManager 来执行和撤销命令。通过使用 shared_ptr,可以确保 Receiver 和 ConcreteCommand 对象的生命周期得到妥善管理,即使在命令被撤销后也不会导致内存泄漏。

3.2 命令模式应用需要设计一组命令,并且命令之间可以相互组合的场景

在C++中,命令模式可以用于设计一组命令,并允许这些命令之间可以相互组合,形成复合命令。当需要执行一系列操作作为一个单一命令时,这种模式特别有用。如下为样例代码:

#include <iostream>  
#include <memory>  
#include <vector>  // 命令接口  
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;
};// 具体命令类A  
class CommandA : public Command {
public:void execute() override {std::cout << "Executing CommandA" << std::endl;}
};// 具体命令类B  
class CommandB : public Command {
public:void execute() override {std::cout << "Executing CommandB" << std::endl;}
};// 复合命令类  
class CompositeCommand : public Command {
public:void addCommand(std::shared_ptr<Command> cmd) {commands.push_back(cmd);}void execute() override {for (const auto& cmd : commands) {cmd->execute();}}private:std::vector<std::shared_ptr<Command>> commands;
};int main() 
{// 创建具体命令对象  auto cmdA = std::make_shared<CommandA>();auto cmdB = std::make_shared<CommandB>();// 创建复合命令对象  auto compositeCmd = std::make_shared<CompositeCommand>();// 将具体命令添加到复合命令中  compositeCmd->addCommand(cmdA);compositeCmd->addCommand(cmdB);// 执行复合命令  compositeCmd->execute();return 0;
}

上面代码的输出为:

Executing CommandA
Executing CommandB

在这个示例中:

  • Command 是一个抽象基类,定义了命令的接口,即 execute 方法。
  • CommandA 和 CommandB 是具体命令类,分别实现了 execute 方法以执行特定的操作。
  • CompositeCommand 是一个复合命令类,它包含一个命令对象的 vector 容器。通过 addCommand 方法,可以将多个具体命令或复合命令添加到复合命令中。当调用 execute 方法时,它会依次执行容器中的每个命令。

在 main 函数中,创建了两个具体命令对象 cmdA 和 cmdB,然后创建了一个复合命令对象 compositeCmd。接着,将 cmdA 和 cmdB 添加到 compositeCmd 中。最后,调用 compositeCmd 的 execute 方法来执行复合命令,这将依次执行 cmdA 和 cmdB。

4 命令模式的优点与缺点

C++ 命令模式的优点主要包括:

(1)封装性: 每个命令都被封装起来,客户端无需知道命令具体是怎么执行的,只需调用相应的命令。这使得命令模式能够很好地隐藏实现细节,使系统更加模块化和可维护。

(2)扩展性: 命令模式符合开闭原则,即对扩展开放,对修改关闭。这意味着可以轻松地添加新命令而无需修改现有代码。

(3)支持撤销和重做: 命令模式可以方便地实现对请求的撤销和重做,这对于需要支持撤销操作的系统来说非常有用。

(4)请求排队和记录: 命令模式可以较容易地设计一个命令队列,以及在需要的情况下将命令记入日志。

(5)解耦: 命令模式将请求一个操作的对象与知道如何执行一个操作的对象分隔开,降低了系统的耦合度。

然而,C++ 命令模式也存在一些缺点:

(1)可能产生过多的具体命令类: 对于每一个命令,都需要设计一个具体命令类,这可能导致系统中存在大量的具体命令类,增加了系统的复杂性。

(2)可能导致过度设计: 在一些简单的场景下,使用命令模式可能过于复杂,导致过度设计,增加不必要的开销。

相关文章:

突破编程_C++_设计模式(命令模式)

1 命令模式的基本概念 C 命令模式是一种设计模式&#xff0c;它允许将请求封装为一个对象&#xff0c;从而可以用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。命令模式的主要目的是将请求封装为对象&#xff0c;从而可…...

LeetCode102题:二叉树的层序遍历(python3)

代码思路&#xff1a;使用队列先进先出的特性&#xff0c;queue[]不为空进入for循环&#xff0c;tmp存储每层的节点&#xff0c;将结果添加至res[]中。 python中使用collections中的双端队列deque()&#xff0c;其popleft()方法可达到O(1)时间复杂度。 class Solution:def lev…...

linux服务器保存git账号密码命令

1.保存git账号密码 git config --global credential.helper store 2.查看git账号密码 cd回车 ls -a cat .git-credentials 步骤: 先输入 git config --global credential.helper store 然后进入代码目录&#xff0c;git pull 会提示输入git账号、密码&#xff0c;因为我们提前输…...

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的田间杂草检测系统(深度学习模型+UI界面+Python代码+训练数据集)

摘要&#xff1a;开发用于田间杂草识别的系统对提高农业运营效率和提升作物产出至关重要。本篇文章详尽阐述了如何应用深度学习技术开发一个用于田间杂草识别的系统&#xff0c;并附上了完备的代码实现。该系统基于先进的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5…...

java Lambda表达式如何支持静态方法引用

java Lambda表达式如何支持静态方法引用 在Java中&#xff0c;Lambda表达式支持静态方法引用&#xff0c;允许你直接使用静态方法作为Lambda表达式的实现。静态方法引用使用类名和方法名来引用静态方法。 下面是一个简单的示例&#xff0c;展示了如何在Lambda表达式中使用静态…...

SpringMVC04、Controller 及 RestFul

4、Controller 及 RestFul 4.1、控制器Controller 控制器复杂提供访问应用程序的行为&#xff0c;通常通过接口定义或注解定义两种方法实现。控制器负责解析用户的请求并将其转换为一个模型。在Spring MVC中一个控制器类可以包含多个方法在Spring MVC中&#xff0c;对于Contr…...

【机器学习300问】33、决策树是如何进行特征选择的?

还记得我在【机器学习300问】的第28问里谈到的&#xff0c;看决策树的定义不就是if-else语句吗怎么被称为机器学习模型&#xff1f;其中最重要的两点就是决策树算法要能够自己回答下面两问题&#xff1a; 该选哪些特征 特征选择该选哪个阈值 阈值确定 今天这篇文章承接上文&…...

剑指offer C ++双栈实现队列

1. 基础 队列&#xff1a;先进先出&#xff0c;即插入数据在队尾进行&#xff0c;删除数据在队头进行&#xff1b; 栈&#xff1a;后进先出&#xff0c;即插入与删除数据均在栈顶进行。 2. 思路 两个栈实现一个队列的思想&#xff1a;用pushStack栈作为push数据的栈&#xff…...

【YOLOv9】训练模型权重 YOLOv9.pt 重新参数化轻量转为 YOLOv9-converted.pt

【YOLOv9】训练模型权重 YOLOv9.pt 重新参数化轻量转为 YOLOv9-converted.pt 1. 模型权重准备2. 模型重新参数化2.1 文件准备2.2 参数修改2.3 重新参数化过程 3. 重新参数化后模型推理3.1 推理超参数配置3.2 模型推理及对比 4. onnx 模型导出&#xff08;补充内容&#xff09;4…...

Zookeeper搭建

目录 前言 初了解Zookeeper 搭建 准备 配置Zookeeper 前言 今天来介绍Zookeeper的搭建&#xff0c;其实Zookeeper的搭建很简单&#xff0c;但是为什么还要单独整一节呢&#xff0c;这就不得不先了解Zookeeper有什么功能了&#xff01;而且现在很火的框架也离不开Zookeepe…...

2.Datax数据同步之Windows下,mysql和sqlserver之间的自定义sql文数据同步

目录 前言步骤操作大纲步骤明细mysql 至 sqlServersqlServer 至 mysql执行同步语句中报 前言 上一篇文章实现了不同的mysql数据库之间的数据同步&#xff0c;在此基础上本篇将实现mysql和sqlserver之间的自定义sql文数据同步 准备工作&#xff1a; JDK(1.8以上&#xff0c;推…...

commonjs和esmodule

commonjs的模块导出和引用写法&#xff1a; lib.js 导出一个模块 let a 1 let b 2 function aPlus1() {return a } module.exports {a,b,aPlus1 } index.js引用一个模块 const {a,b,aPlus1} require(./lib.js) console.log(hh:,a) esmodule的模块导出和引用方法&#x…...

Android的编译系统

安卓的编译真的太多吐槽的地方了&#xff0c;有必须到croot下编译的&#xff0c;有随便改个.c就要七八分钟编译的。我有时候真的不知道这么多开发人员是怎么挺过来的。 今晚简单看看这个编译系统soong吧。 算了&#xff0c;下面这个写的很好了&#xff0c;我先看看吧。。。 …...

Midjourney指控Stability AI夜袭数据,网络风波一触即发

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

JVM知识整体学习

前言&#xff1a;本篇没有任何建设性的想法&#xff0c;只是我很早之前在学JVM时记录的笔记&#xff0c;只是想从个人网站迁移过来。文章其实就是对《深入理解JVM虚拟机》的提炼&#xff0c;纯基础知识&#xff0c;网上一搜一大堆。 一、知识点脑图 本文只谈论HotSpots虚拟机。…...

蓝桥杯--日期统计

目录 一、题目 二、解决代码 三、代码分析 ​四、另一种思路 五、关于set文章推荐 一、题目 二、解决代码 #include <bits/stdc.h> using namespace std; int main() {int arr[100] { 5,6,8,6,9,1,6,1,2,4,9,1,9,8,2,3,6,4,7,7,5,9,5,0,3,8,7,5,8,1,5,8,6,1,8,3,0,…...

[leetcode~dfs]1261. 在受污染的二叉树中查找元素

给出一个满足下述规则的二叉树&#xff1a; root.val 0 如果 treeNode.val x 且 treeNode.left ! null&#xff0c;那么 treeNode.left.val 2 * x 1 如果 treeNode.val x 且 treeNode.right ! null&#xff0c;那么 treeNode.right.val 2 * x 2 现在这个二叉树受到「污…...

PyQt5使用

安装Pyqt5信号与槽使用可视化界面编辑UI (Pyside2)ui生成之后的使用(两种方法)1 ui转化为py文件 进行import2 动态调用UI文件 安装Pyqt5 pip install pyqt5-tools这时候我们使用纯代码实现一个简单的界面 from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButto…...

利用GPT开发应用005:Codex、Turbo、ChatGPT、GPT-4

文章目录 一、GPT-3 Codex二、GPT-3.5 Turbo二、ChatGPT三、GPT-4 一、GPT-3 Codex 2022年3月&#xff0c;OpenAI 发布了 GPT-3 Codex 的新版本。 这个新模型具有编辑和插入文本的能力。它们是通过截至 2021 年 6 月的数据进行训练的&#xff0c;并被描述为比之前版本更强大。到…...

制造行业大数据应用:四大领域驱动产业升级与智慧发展

一、大数据应用&#xff1a;制造行业的智慧引擎 随着大数据技术的不断突破与普及&#xff0c;制造行业正迎来一场前所未有的变革。大数据应用&#xff0c;如同智慧引擎一般&#xff0c;为制造行业注入了新的活力&#xff0c;推动了产业升级与创新发展。 二、大数据应用在制造行…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...