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

news2024/10/1 17:31:23

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.coloradmin.cn/o/2065804.html

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

相关文章

百度云语音识别demo验证

百度云语音识别调研 百度语音识别API: https://ai.baidu.com/ai-doc/SPEECH/il9mh8cjb 1: 注册登录百度账户. 2:打开控制台选择语音技术. 3: 领取免费额度(个人账户.企业账户不确定是否免费额度一样) 4: 由于开发测试使用,可以选择不需要包名. 5: 下载demo程序: https://g…

pat1074 k个一组反转链表

是我的错觉吗 直接把%d赋值给数组小标&#xff0c;不能把值副进去&#xff1f; 可是这样可以&#xff1f; 乱套了这 同样的代码 所以暂时将你眼睛闭了起来 所以还是好好老实两次赋值 。。。 写糊了已经 注意这句话 链表中部翻转&#xff1a; 不带dummy真的好麻烦且头…

苹果手机照片格式heic怎么改jpg?教你3招快速转换

苹果手机照片格式heic怎么改jpg&#xff1f;将苹果手机照片格式从HEIC转为JPG&#xff0c;在日常使用中带来了极大便利。HEIC格式虽高效节省空间&#xff0c;但兼容性不如JPG广泛。转为JPG后&#xff0c;照片能轻松在多数设备、软件和在线平台上分享、打印或编辑&#xff0c;无…

SmartGit-Git版本控制系统的图形化客户端

SmartGit&#xff1a; SmartGit是一款免费的、专业的Git版本控制系统的图形化客户端。它适用于Windows、Mac和Linux等多种操作系统&#xff0c;提供了直观的用户界面和丰富的功能。支持创建、克隆、推送、拉取、合并和管理Git仓库&#xff0c;以及强大的分支管理功能。还提供了…

武汉流星汇聚:亚马逊中国卖家精准布局,万圣节装饰热销引领潮流

随着秋风渐起&#xff0c;万圣节的脚步虽还远在三个月之后&#xff0c;但消费者对于节日氛围的营造与期待已悄然升温。在亚马逊这一全球电商巨头的平台上&#xff0c;万圣节相关产品的搜索热潮正以前所未有的速度席卷而来&#xff0c;为中国卖家提供了又一个展示实力、捕捉商机…

大模型概念入门:探索这一AI技术的奥秘

一、引言 ChatGPT、Open AI、大模型、提示词工程、Token、幻觉等人工智能的黑话&#xff0c;在2023年这个普通却又神奇的年份里&#xff0c;反复的冲刷着大家的认知。让一部分人彻底躺平的同时&#xff0c;让另外一部分人开始焦虑起来&#xff0c;生怕在这个人工智能的奇迹之年…

无人机搭载的高压喷水清洗技术详解

随着城市化进程的加速&#xff0c;高空建筑、桥梁、电力设施等清洁维护问题日益凸显。传统的人工清洗方式不仅效率低下、成本高昂&#xff0c;还存在高空作业安全风险。无人机搭载的高压喷水清洗技术应运而生&#xff0c;以其独特的优势成为解决这一难题的新方案。该技术通过无…

不看表了

前段时间重温了一遍刘德华在1992年拍的《赌城大亨-新哥传奇》&#xff0c;里面叶倩文唱的《不了情》&#xff0c;顾媚唱的太凄&#xff0c;小凤姐唱的太醇&#xff0c;而莎莉叶倩文唱的太有大时代风云际会儿女情长味道。 刘德华华仔和邱淑贞豆豆&#xff0c;合作拍摄的两部大佬…

网络空间安全中的数字孪生技术研究

源自&#xff1a;系统仿真学报 作者&#xff1a;任乾坤,熊鑫立,刘京菊&#xff0c;姚倩 注&#xff1a;若出现显示不完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 人工智能、大数据、多模态大模型、计算机视觉、自然语言处理、数字孪生、深度强化学习…

软件工程造价师习题练习 19

1.在 A 系统中&#xff0c;用户可以对白名单进行新增、删除与查询的操作。在查询的过程中&#xff0c;用户在搜索框内录入关键字&#xff0c;这个“录入”可以识别为非基本过程。 正确 错误 在A系统中&#xff0c;用户对白名单进行新增、删除与查询的操作构成了系统的主要功能…

Scheme3.0标准之重要特性及用法实例(三十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

【Python进阶(八)】——数据框

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

JetBrains 开发工具——学生授权免费申请指南

2022 JetBrains 开发工具——学生授权免费申请指南 | JetBrains 博客https://blog.jetbrains.com/zh-hans/blog/2022/08/24/2022-jetbrains-student-program/ 第一次下载datagrip试用期一个月&#xff0c;在读学生申请试用期1年&#xff0c;可续期。 参考第一个文档申请学生认…

黑神话悟空配置要求是什么?

黑神话悟空最低配置要求是&#xff1a;操作系统Windows1064位、处理器Inteli5-8400或AMDRyzen51600、内存8GBRAM、图形NVIDIAGeForceGTX1060或AMDRadeonRX580、存储空间需要最少50GB可用空间。推荐配置&#xff1a;操作系统Windows1064位、处理器IntelCorei7-9700或AMDRyzen555…

内网渗透的风行者—Yasso

Yasso &#xff1a; Yasso&#xff0c;让内网渗透变得简单而高效。- 精选真开源&#xff0c;释放新价值。 概览 Yasso是由sairson精心打造的内网渗透辅助工具集&#xff0c;它为网络安全专家和渗透测试人员提供了一个功能强大的工作平台。在面对错综复杂的网络环境时&#xff…

uniapp实现区域滚动、下拉刷新、上滑滚动加载更多

背景&#xff1a; 在uniapp框架中&#xff0c;有两种实现办法。第1种&#xff0c;是首先在page.json中配置页面&#xff0c;然后使用页面的生命周期函数&#xff1b;第2种&#xff0c;使用<scroll-view>组件&#xff0c;然后配置组件的相关参数&#xff0c;包括但不限于&…

【SpringBoot】电脑商城-07-上传头像

基于SpringMVC的文件上传 1 MultipartFile接口 MultipartFile接口常用的的API见下表&#xff1a; 方法功能描述String getOriginalFilename()获取上传文件的原始文件名&#xff0c;即该文件在客户端中的文件名boolean isEmpty()判断上传的文件是否为空&#xff0c;当没有选择…

flowable源码解读——并行多实例节点任务是否是顺序生成

最近在项目开发中需要在多实例开始监听里修改一个全局的计数变量&#xff0c;不太确定并行多实例任务在底层引擎是顺序生成还是并行生成的&#xff0c;如果是顺序生成的则不影响&#xff0c;如果是并行生成 则修改一个全局的计数变量就会出现数据错误问题&#xff0c;查阅了flo…

JVM的原理和性能调优

java是如何做到跨平台&#xff1f; 将java文件通过javac编辑到JVM中&#xff0c;由JVM根据操作系统&#xff08;Windows&#xff0c;Linux&#xff09;的需要&#xff0c;生成出相对应的二进制文件&#xff0c;从而达到跨平台的特性。 JVM的组成 将java文件通过javac编译成clas…

逻辑回归C参数选择,利用交叉验证实现

目录 前言 一、C参数 二、交叉验证 1.交叉验证是什么 2.交叉验证的基本原理 3.交叉验证的作用 4.常见的交叉验证方法 三、k折交叉验证 四、C参数和k折交叉验证的关系 五、代码实现 1.导入库 2.k折交叉验证选择C参数 3.建立最优模型 总结 前言 逻辑回归&#xff0…