C++ 设计模式——命令模式

C++ 设计模式——命令模式

    • C++ 设计模式——命令模式
      • 主要组成部分
      • 构建过程
      • 命令模式 UML 图
        • UML 图解析
      • 命令模式的优点
      • 命令模式的缺点
      • 命令模式适用场景
      • 总结
      • 完整代码

C++ 设计模式——命令模式

命令(Command)模式是一种行为型模式,它将请求封装为对象,从而使您能够使用不同的请求、排队请求或记录请求,以及支持可撤销的操作。

主要组成部分

  1. 命令接口(Command):定义了一个执行操作的接口,通常包含一个核心方法,该方法负责执行请求。
  2. 具体命令(ConcreteCommand):实现了命令接口,并定义了与接收者之间的绑定关系。在执行请求时,它会调用接收者的方法。
  3. 接收者(Receiver):知道如何实施与执行相关的操作。它是执行请求的实际对象。
  4. 调用者(Invoker):持有命令对象,并在适当的时候调用它们。它通常不直接执行操作,而是通过命令对象来执行。
  5. 客户端(Client):创建具体命令对象,并将其与接收者关联。客户端负责创建和管理命令对象。

构建过程

命令模式的构建过程,以点菜为例。在这个场景中,命令模式使得服务员(调用者)和厨师(接收者)之间的交互更加灵活和清晰。服务员不需要知道厨师如何制作菜品,只需传递订单便签即可。而厨师只需按照订单便签制作相应的菜品。这样,整个点餐流程更加模块化和易于管理。

  1. 命令接口(Command)

    • 代码定义了一个名为Command的抽象类,它代表了一个通用的命令接口。

    • 在点菜场景中,这个接口可以被看作是服务员用来传递给厨师的一个“订单便签”,它告诉厨师需要做什么菜。

    class Command
    {
    public:Command(Cook* pcook) : m_pcook(pcook) {}virtual ~Command() { delete m_pcook; }virtual void Execute() = 0;protected:Cook* m_pcook; // 接收者
    };
    
  2. 具体命令(ConcreteCommand)

    • CommandFishCommandMeat类是Command接口的具体实现,它们代表具体的订单,比如“做红烧鱼”和“做锅包肉”。
    class CommandFish : public Command
    {
    public:CommandFish(Cook* pcook) : Command(pcook) {}void Execute() override{m_pcook->cook_fish(); // 调用接收者的方法}
    };class CommandMeat : public Command
    {
    public:CommandMeat(Cook* pcook) : Command(pcook) {}void Execute() override{m_pcook->cook_meat(); // 调用接收者的方法}
    };
    
  3. 接收者(Receiver)

    • Cook类代表接收者,即厨师,他知道如何制作菜品。
    • 在点菜场景中,厨师根据服务员传递的订单便签(命令对象)来制作菜品。
    class Cook
    {
    public:void cook_fish(){cout << "做一盘红烧鱼菜品" << endl;}void cook_meat(){cout << "做一盘锅包肉菜品" << endl;}
    };
    
  4. 调用者(Invoker)

    • Waiter类代表调用者,即服务员,他负责接收顾客的订单并传递给厨师。
    • 服务员保存了一个命令列表,并负责在适当的时机调用这些命令,通知厨师开始制作菜品。
    class Waiter
    {
    public:void AddCommand(Command* pcommand){m_commlist.push_back(pcommand);}void Notify(){for (auto iter = m_commlist.begin(); iter != m_commlist.end(); ++iter){(*iter)->Execute(); // 调用命令的执行方法}}private:std::list<Command*> m_commlist; // 命令列表
    };
    
  5. 客户端(Client)

    • main函数作为客户端,模拟顾客下订单的过程。
    • 客户端创建服务员和厨师对象,以及具体的命令对象,并将命令对象添加到服务员的订单列表中。
    int main()
    {// 1. 客户端创建接收者 厨师Cook* cook1 = new Cook();Cook* cook2 = new Cook();// 2. 客户端创建具体命令对象并设置接收者 订单列表Command* pcmd1 = new CommandFish(cook1);Command* pcmd2 = new CommandMeat(cook2);// 3. 客户端将命令对象传递给调用者 服务员 Waiter* pwaiter = new Waiter();pwaiter->AddCommand(pcmd1);pwaiter->AddCommand(pcmd2);// 4. 调用者请求执行操作 服务员通知厨师pwaiter->Notify();// 释放资源delete pwaiter;delete pcmd2;delete pcmd1;return 0;
    }
    

命令模式 UML 图

命令模式UML图

UML 图解析
  1. Receiver(接收者类):知道如何实施与执行请求相关的操作,这里指 Cook 类。提供了对请求的业务处理接口(cook_fishcook_meat)。
  2. Invoker(调用者类):请求的发送者,通过命令对象来执行请求,这里指 Waiter 类。该类只与抽象命令类 Command 之间存在关联关系。
  3. Command(抽象命令类):声明执行操作的接口,这里指的是 Command 类。在其中声明了用于执行请求的 Execute 方法。
  4. ConcreteCommand(具体命令类):抽象命令类的子类,这里指的是 CommandFish 类和 CommandMeat 类。类中实现了执行请求的 Execute 方法来调用接收者类 Cook 中的相关操作。
  5. Client(客户端):创建具体的命令类对象并设定它的接收者。这里指main主函数中的几行代码。

命令模式的优点

  • 解耦:命令模式将请求的发送者和接收者解耦,降低了它们之间的耦合度。
  • 可扩展性:可以轻松添加新的命令而不修改现有代码。
  • 支持撤销操作:可以实现命令的撤销和重做功能。
  • 支持排队请求:可以将请求排队并在需要时执行。

命令模式的缺点

  • 命令类的数量可能会增加:如果有很多操作,可能会导致命令类的数量激增。
  • 复杂性增加:引入命令模式可能会增加系统的复杂性。

命令模式适用场景

  • 请求解耦:当需要将请求的发送者(调用者)与请求的接收者(接收者)解耦时。比如,在图形用户界面中,按钮点击事件可以通过命令模式实现,按钮不需要直接调用处理逻辑,而是通过命令对象进行。
  • 支持撤销和重做操作:当需要实现撤销和重做功能时。命令对象可以被存储在历史记录中,用户可以随时撤销或重做操作。例如,在文本编辑器中,用户可以撤销或重做输入的文本操作。
  • 请求排队或记录请求:当需要将请求排队或记录请求时。命令模式允许将多个请求存储在一个队列中,稍后统一处理。这在任务调度或异步处理场景中非常有用。
  • 实现宏命令:当需要实现宏命令(多个命令的组合)时。可以将多个命令组合成一个命令对象,方便一次性执行多个操作。例如,在游戏中,可以将多个动作(如移动、攻击、跳跃)组合成一个宏命令。
  • 动态命令:当需要在运行时动态创建和组合命令时。命令模式允许在运行时根据用户输入或其他条件生成新的命令对象,增强系统的灵活性。
  • 日志记录:当需要记录操作日志时。通过命令对象,可以记录每个操作的详细信息,方便后续审计和分析。
  • 多线程环境:在多线程环境中,命令模式可以帮助管理任务的执行,确保线程安全地执行命令。

总结

命令模式通过将请求封装为对象,使得请求的发送者和接收者之间的耦合度降低,增强了系统的灵活性和可扩展性。它不仅适用于简单的命令执行场景,还可以扩展到复杂的请求管理和操作历史记录等场景。

完整代码

#include <iostream>
#include <list>
using namespace std;//厨师类
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;
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 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; //菜品列表,每道菜品作为一项,如果一个便签中有多个菜品,则这里将包含多项
};int main()
{//一次性在便签上写下多道菜品Command* pcmd1 = new CommandFish(new Cook());Command* pcmd2 = new CommandMeat(new Cook());Waiter* pwaiter = new Waiter();//把多道菜品分别加入到菜品列表中pwaiter->AddCommand(pcmd1);pwaiter->AddCommand(pcmd2);//服务员一次性通知厨师做多道菜pwaiter->Notify();// 释放资源delete pwaiter;delete pcmd2;delete pcmd1;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/52338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据中台架构设计

由于当前项目需要对接多个不同的数据源&#xff0c;同时涉及到多端处理&#xff0c;而且需要考虑海量数据处理&#xff0c;还有总部与分部架构部署问题&#xff0c;因而整体技术栈倾向于大数据和分表分库式处理数据层接入问题。 简单讲&#xff0c;项目分为数据中台和业务中台…

[新手入门]1台电脑+1个电视+2个软件(sunshine+moonlight) 解决黑神话悟空没有hdmi线的痛...

sunshinemoonlight 解决黑神话悟空 本地串流投屏 背景:偶然间在B站发现了sunshinemoonlight方案,替代hdmi线,做本地串流...于是心灵手巧的我开始尝试踩坑之路:1.准备安装包2.开始安装2.1 笔记本windows安装sunshine2.2 遇到了第一个坑.Fatal: ViGEmBus is not installed or run…

docke进阶---镜像迁移、容器的ip地址、端口映射和持久化

1.镜像的迁移 1.镜像打包 #查看镜像有一个centos的镜像 [rootdocker0 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos latest 5d0da3dc9764 2 years ago 231MB 3查看帮助文件 docker --help save Save one or more…

【Unity】移动端草海解决方案

草海是开放大世界渲染的必不可少的因素&#xff0c;Unity 原生的 Terrain 草海效率较低&#xff0c;而且无法与 RVT 结合起来&#xff0c;无法在移动端上实现。因此我们自己搓出来一套草海系统&#xff0c;使用 C# 多线程辅助运算&#xff0c;并能支持割草、烧草等进阶玩法。草…

数据库内容保密检查系统:及时发现“潜在”安全威胁

日前&#xff0c;国内专注于保密与非密领域的分级保护、等级保护、业务连续性安全和大数据安全产品解决方案与相关技术研究开发的领军企业——国联易安自主研发的国联数据库内容保密检查系统V1.0通过国保局涉密检测&#xff0c;获得涉密信息系统产品检测证书。其主要具备以下主…

输入名字转换成对象

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>输入名字转换成对象</title> </head>…

力扣算法总结

直接放别人简单易懂的总结&#xff0c;比我自己描述得好 代码随想录 (programmercarl.com) 崔添翼 (Tianyi Cui) 背包问题九讲 2.0 beta1.2 - jggnice! - 博客园 (cnblogs.com) 1.01背包 优化成一维就是利用之前上一维计算出来的数据&#xff0c;在不损害后面还要用的上一维…

【前端基础篇】JavaScript之BOM介绍

文章目录 浏览器对象模型&#xff08;BOM&#xff09;介绍1. 什么是BOM&#xff1f;2. Window 对象2.1 弹出框2.1.1 警告框2.1.2 确认框2.1.3 提示框 2.2 定时事件2.2.1 延时器2.2.2 定时器 2.3 Window 对象其他常用属性与方法2.3.1 获取窗口尺寸2.3.2 打开新窗口与关闭窗口2.3…

【JS|第25期】探索HTTP POST请求:请求体的演变与应用

日期&#xff1a;2024年8月16日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

iOS RunLoop

一:什么是Runloop Runloop&#xff0c;正如其名&#xff0c;loop表示某种循环&#xff0c;和run放在一起就表示一直在运行着的循环 二:Runloop的创建? iOS并没有提供Runloop的创建方法,因为创建了现场自然会有一个Runloop. 所以只有获取Runloop的方法: NSRunLoop * runloo…

C# 循环访问目录树详解与示例

文章目录 一、目录树遍历的概念二、使用System.IO命名空间三、DirectoryInfo和FileInfo类四、递归遍历目录树五、示例&#xff1a;列出目录树中的所有文件和文件夹六、异常处理七、迭代方法八、总结 在C#中&#xff0c;访问文件系统是常见的需求之一。有时我们需要遍历目录树以…

kafak集群搭建-基于zookeeper方式

kafak集群搭建-基于zookeeper方式 1、准备3个kafka2、修改配置文件2.1、修改配置文件 3、启动测试3.1、先启动zookeeper3.2、再启动三台kafka 4、SpringBoot集成kafka集群4.1、项目配置文件application.yml4.2、kafka配置类4.3、启动SpringBoot程序 5、kafka集群架构分析6、查看…

【kubernetes】相关pod的创建和命令

【书写方法】&#xff1a; 管理使用k8s集群时&#xff0c;创建资源的Yaml文件非常重要&#xff0c;如何快速手写呢&#xff1f; 根据命令提示书写&#xff1a; kubectl explain [资源名称]例如打算写pod资源文件时&#xff0c;可查看如下&#xff1a; # 查看pod下所有字段 …

Google Colab快速使用

Google Colab快速使用 1. 引言2. Jupyter笔记本的创建3. 上传代码和数据集4. Colab常规指令 1. 引言 Google Colab是谷歌提供的免费Jupyter&#xff0c;很类似于Linux系统这些在终端界面操纵的感觉&#xff0c;不需要深度学习环境配置就可以使用&#xff0c;完全基于云端运行。…

在Windows Server 2012 R2上安装.NET Framework 3.5

在Windows Server 2012 R2上安装.NET Framework 3.5&#xff0c;可以按照以下步骤进行&#xff1a; 打开服务器管理器&#xff1a; 首先&#xff0c;登录到Windows Server 2012 R2的服务器。然后&#xff0c;打开“服务器管理器”。添加角色和功能&#xff1a; 在“服务器管理…

针对防火墙IPSec业务不通或业务丢包问题,防火墙如何做流量统计、远程抓包、报文示踪

问题描述 针对防火墙IPSec业务不通或业务丢包问题&#xff0c;防火墙如何做流量统计、远程抓包、报文示踪 解决方案 1&#xff09;配置流统和远程抓包用的ACL&#xff1b; system [sysname] acl 3555 [sysname-acl-adv-3555] rule permit icmp source 10.82.100.215 0 destin…

结构型模式之代理模式

一、概述 1、代理模式&#xff1a;给某一个对象提供一个代理或占位符&#xff0c;并由代理对象来控制对原对象的访问。 2、代理对象在客户端和目标对象之间起到中介作用 3、引入一个新的代理对象&#xff0c;代理模式的主要目的是在不改变原始对象接口的前提下&#xff0c;增…

[C语言]一、C语言基础(函数)

G:\Cpp\C语言精讲 6. 函数 6.1函数的基本使用 6.1.1 为什么需要函数 《街霸》游戏中&#xff0c;每次人物出拳、出脚或跳跃等动作都需要编写50-80行的代码&#xff0c;在每次出拳、出脚或跳跃的地方都需要重复地编写这50-80行代码&#xff0c;这样程序会变得很臃肿&#xff…

通过Python绘制不同数据类型适合的可视化图表

在数据可视化中&#xff0c;对于描述数值变量与数值变量之间的关系常见的有散点图和热力图&#xff0c;以及描述数值变量与分类变量之间的关系常见的有条形图&#xff0c;饼图和折线图&#xff0c;可以通过使用Python的matplotlib和seaborn库来绘制图表进行可视化表达&#xff…

如何使用ssm实现ssm框架的购物网站+vue

TOC ssm113ssm框架的购物网站vue 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。…