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

news2024/11/25 10:08:30

我们将Command 和Visitor归为“行为变化”模式。
Command 命令模式与函数对象十分类似,但在C++主流框架中,函数对象(function object)应用的更为广泛。

文章目录

  • 1. “行为变化”模式
    • 1.1 典型模式
  • 2. 动机( Motivation )
  • 3. 模式定义
  • 4. Command 命令模式代码演示
  • 5. 结构( Structure )
  • 6. 要点总结
  • 7. 其他参考

1. “行为变化”模式

  • 在组件的构建过程中,组件行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者之间的松耦合

行为一般是一段代码,代码和对象之间的绑定关系是极其紧密的,是一种天然的编译式绑定。类的成员函数分为两类,一类是虚函数,一类是非虚的或者静态函数,第二类函数是地址编译时静态的绑定,虚函数是运行时的绑定,这些绑定实际上是非常紧耦合的。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者之间的松耦合。

1.1 典型模式

  • Command

  • Visitor

2. 动机( Motivation )

  • 在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合一一比如需要对行为进行“记录、撤销/重(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。

  • 在这种情况下,如何将“行为请求者”与“行为实现者”解耦 ?将一组行为抽象为对象,可以实现二者之间的松耦合。

3. 模式定义

将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

----《设计模式》GoF

4. Command 命令模式代码演示

整体代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;


class Command
{
public:
    virtual void execute() = 0;
};

class ConcreteCommand1 : public Command
{
    string arg;
public:
    ConcreteCommand1(const string & a) : arg(a) {}
    void execute() override
    {
        cout<< "#1 process..."<<arg<<endl;
    }
};

class ConcreteCommand2 : public Command
{
    string arg;
public:
    ConcreteCommand2(const string & a) : arg(a) {}
    void execute() override
    {
        cout<< "#2 process..."<<arg<<endl;
    }
};
        
        
class MacroCommand : public Command
{
    vector<Command*> commands;
public:
    void addCommand(Command *c) { commands.push_back(c); }
    void execute() override
    {
        for (auto &c : commands)
        {
            c->execute();
        }
    }
};
        

        
int main()
{

    ConcreteCommand1 command1(receiver, "Arg ###");
    ConcreteCommand2 command2(receiver, "Arg $$$");
    
    MacroCommand macro;
    macro.addCommand(&command1);
    macro.addCommand(&command2);
    
    macro.execute();

}

代码分析:

Command类中塞了一个execute()虚函数

class Command
{
public:
    virtual void execute() = 0;
};

继承自Command的类构成的对象,表达的就是一个行为对象,如下面所展示的ConcreteCommand1,封装了请求处理过程的参数信息,override execute()函数,类似的有ConcreteCommand2类。

class ConcreteCommand1 : public Command
{
    string arg;
public:
    ConcreteCommand1(const string & a) : arg(a) {}
    void execute() override
    {
        cout<< "#1 process..."<<arg<<endl;
    }
};

我们还可以用一些宏命令,可以把多个命令组合,vector<Command*> commands;使用了类似composite的设计模式,组合命令本身继承自命令对象,实现execute() ,遍历的方式对vector<Command*> commands;所有的子节点进行遍历操作

class MacroCommand : public Command
{
    vector<Command*> commands;
public:
    void addCommand(Command *c) { commands.push_back(c); }
    void execute() override
    {
        for (auto &c : commands)
        {
            c->execute();
        }
    }
};

下面代码ConcreteCommand1 command1(receiver, "Arg ###");ConcreteCommand2 command2(receiver, "Arg $$$");MacroCommand macro;封装出来了command对象。下面的方式还有很多其他好处,一个macro组合命令可以包含多个子命令,最关键的是从此拿着command1、command2、macro,这些都是对象,但又表征的是行为(只有execute()一个函数)。当一个对象表征一个行为,你拿着这个对象可以当做参数来传递,当做字段存储,序列化,存在某些数组结构里等等,这就是所谓灵活化,背后表征的实际是一段代码,这个代码是带有丰富的参数信息。

int main()
{

    ConcreteCommand1 command1(receiver, "Arg ###"); //构成命令
    ConcreteCommand2 command2(receiver, "Arg $$$"); //构成命令
    
    MacroCommand macro; //构成命令组
    macro.addCommand(&command1);
    macro.addCommand(&command2);
    
    macro.execute();//执行

}

5. 结构( Structure )

在这里插入图片描述
上图是《设计模式》GoF中定义的Command 命令模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。
在这里插入图片描述

Command是抽象的命令接口,ConcreteCommand是一些变化的。比如在实现编辑器上的命令,剪切,删除,粘贴等都可以实现成命令对象,之后就可以对这些存储在一个栈结构里,实现redo/undo操作。这就是一旦把行为对象话之后可能获得的好处。

真正要实现redo/undo或者特殊的针对对象的处理逻辑,那就是另外的业务逻辑,可能复杂也可能简单,基本思想要把行为封装为对象

6. 要点总结

  • Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”

  • 实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。通过使用Composite模式,可以将多个命封装为一个“复合命令”MacroCommand。

  • Command模式与C++中的函数对象有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格,但有性能损失;C++函数对象以函数签名来定义行为接口规范,更灵活,性能更高。

94年定义Command模式的时候,C++函数对象并没有广为使用,泛型编程是98年,后来人们发现Command模式与C++中的函数对象十分类似,在某些场合C++函数对象要优于Command模式。

Command以面向对象中的“接口-实现”来定义行为接口规范virtual void execute() = 0;,函数是execute()、返回值为void,参数是空,继承具体时候必须遵守。由于是虚函数,会有性能损失。

C++函数对象以函数签名来定义行为接口规范:尤其是使用模板之后,只需要参数和返回值一致即可,名字无所谓,所以更为灵活,接口规范是隐式的。由于利用了模板是编译时多态技术,Command中是运行时虚函数动态遍析 for (auto &c : commands) { c->execute(); }

基于前面提到的2个区别,在C++主流框架中,函数对象(function object)应用的更为广泛(在C++的所有设计中性能优先),但是Command这种思想在其他语言得到了极大的应用(与interator的应用情况类似)

C++函数对象是利用了C++非常好的特征:可以重载括号;调用操作符;和泛型编程结合在一起,很多时候使用的是模板的编译式绑定,而此处使用的是运行时绑定。

7. 其他参考

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

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

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

相关文章

Java调用GDAL实现融合有相同字段属性的多边形矢量数据的一种方法

目录 一、写在文章前 二、实现思路 三、实现过程 1.打开矢量数据 2.生成融合结果 3.不融合不相邻的几何图形 4.融合后的几何图形写入到新的图层 5.融合的效果 四、完整的示例 一、写在文章前 如下图所示&#xff0c;融合&#xff08;dissolve&#xff09;具有相同字段属…

Linux中搭建coturn服务器

1、下载coturn源码 git clone https://github.com/coturn/coturn.git2、进入到coturn路径下&#xff0c;执行一下命令。 ./configure出现以下错误&#xff1a; 问题1&#xff1a;ERROR: OpenSSL Crypto development libraries are not installed properly in required locati…

浅谈安科瑞无线测温产品在南非某变电站的应用

摘要&#xff1a;随着电力工业的发展&#xff0c;对设备的安全性、可靠性要求越来越高。在这种条件下&#xff0c;高压设备的无线测温系统应运而生。这种技术是将内置电池或电流感应和无线发射模块的测温传感器安装于各测温点&#xff0c;由于其体积小&#xff0c;且无需任何接…

CHS零壹视频恢复程序监控版/海康版/大华版深入扫描功能演示

安防文件系统是一种嵌入式文件系统&#xff0c;一般情况下监控版程序扫描会基于文件系统进行扫描&#xff0c;如果想更深入的扫描一些数据建议开启深入扫描功能&#xff0c;具体方法如下: 适用版本:监控版/海康版/大华版/专业版/高级版 作用&#xff1a;舍弃嵌入式文件系统直…

手机端抓包流程

一.Charles 抓包 1.创建模拟器 2.配置证书 3.打开开发者工具中的usb调试 4.下载xposed安装 5.安装模块 破解app反代理 使用XposedJustTrustMe突破SSL Pinning验证_justtrustme模块官网-CSDN博客 配置流程 手机APP数据包抓包分析_socksdroid下载-CSDN博客 二.HttpCanary抓…

【CIO人物展】申菱环境CIO吴斌:制造业智能化转型,从3个“三”开始

吴斌 本文由申菱环境CIO吴斌投递并参与《2023中国数智化转型升级优秀CIO》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 近日&#xff0c;苏州市举办了苏州市智能制造生态大会暨苏州市智能制造产业联盟第二届二次会员大会&#xff0c;苏州市工信局副局长万资…

【观察】从口袋到云端全景式AI创新,联想“全栈智能”再升级

知名科技杂志《连线》创始主编凯文凯利曾预测&#xff1a;“在未来的 100 年里&#xff0c;人工智能将超越任何一种人工力量&#xff0c;将人类引领到一个前所未有的时代。” 确实如此&#xff0c;犹如历史上蒸汽机、电力、计算机和互联网等通用技术一样&#xff0c;近20年来&a…

哪个女人不是在事业和家庭间走钢丝?

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 小黑 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩天津录音间 这是一场因2023年诺贝尔经济学奖获得者克劳迪娅戈尔丁著作《事业还是家庭&#xff1f;女性追求平等的百年…

12 _ 排序(下):如何用快排思想在O(n)内查找第K大元素?

上一节我讲了冒泡排序、插入排序、选择排序这三种排序算法,它们的时间复杂度都是O(n2),比较高,适合小规模数据的排序。这里会介绍两种时间复杂度为O(nlogn)的排序算法,归并排序和快速排序。这两种排序算法适合大规模的数据排序,比上一节讲的那三种排序算法要更常用。 归并…

PDF 表单直接保存到您的文档中--TX Text Control

TX Text Control .NET Server for ASP.NET Document Viewer 32.0.2 允许用户保存包含已填写表单字段的文档&#xff0c;从而更轻松地协作和共享信息。 TX Text Control .NET Server for ASP.NET 是一个适用于 ASP.NET 和 ASP.NET Core 的综合服务器端文档处理库。功能包括 PDF …

5.2 用户数据报协议UDP

思维导图&#xff1a; 课程笔记&#xff1a;5.2 用户数据报协议UDP 5.2.1 UDP概述 一、UDP基本概念 无连接协议&#xff1a;UDP是一个简单的面向数据报的传输层协议&#xff0c;不需要在数据传输前建立连接&#xff0c;故减少开销和延迟。复用/分用&#xff1a;UDP允许多个应…

记录rider编辑器快速文档 中英文显示的问题

起初是不同的项目里快速文档一个项目显示中文 一个项目显示英文 搞了很久不知道哪里的原因 偶然灵机一动,点开了下面docs.microsoft.com的地址进去一看 发现一个是4.6的文档 一个是4.6.1的文档 所以去项目属性里 切换了framework的版本. 然后汉化就好了 纯属强迫症,而且…

shell script 学习案例

或许进度最快的方法就是先抄袭&#xff0c;在改进&#xff0c;哈哈。学习下各个案例吧 第一&#xff1a;写过程序的都知道&#xff0c;第一个学的就是输出"Hello World!",那么学习shell也是一样&#xff0c;上案例 因为有脚本文件&#xff0c;所有同时 开了两个终端…

13 _ 线性排序:如何根据年龄给100万用户数据排序?

前两节中,着重分析了几种常用排序算法的原理、时间复杂度、空间复杂度、稳定性等。这节,将讲三种时间复杂度是O(n)的排序算法:桶排序、计数排序、基数排序。因为这些排序算法的时间复杂度是线性的,所以我们把这类排序算法叫作线性排序(Linear sort)。之所以能做到线性的时…

光刻机之父林本坚发声,中国芯片可自主研发5纳米,美国拦不住

外媒报道指早已退休的台积电前研发副总林本坚接受采访的时候表示&#xff0c;美国无法阻止中国研发先进工艺&#xff0c;还认为中国可以利用现有设备研发更先进的5纳米工艺&#xff0c;他的表态让外媒相当吃惊。 林本坚如此说法在中国的华为研发出相当于7纳米工艺的麒麟9000S之…

Linux常用命令——chfn命令

在线Linux命令查询工具 chfn 用来改变finger命令显示的信息 补充说明 chfn命令用来改变finger命令显示的信息。这些信息都存放在/etc目录里的passwd文件里。若不指定任何选项&#xff0c;则chfn命令会进入问答式界面。 语法 chfn(选项)(参数)选项 -f<真实姓名>或-…

玩转硬件之Micro:bit的玩法(三)——计步器

随着技术的发展&#xff0c;现在智能手机和智能手表已经走进千家万户&#xff0c;所以大家对于计步器可能不陌生&#xff0c;计步器是一种用于计算行走步数的装置。它通常是一个小型电子设备&#xff0c;可以佩戴在身体上&#xff0c;如腕带、腰带或口袋中。计步器通过感应人体…

代码随想录算法训练营第四十一天丨 动态规划part04

01背包理论基础 见连接&#xff1a;代码随想录 416. 分割等和子集 思路 01背包问题 背包问题&#xff0c;大家都知道&#xff0c;有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解…

matlab 计算Ax=b的解,解线性方程组的现成工具

只写了最简单的方式&#xff0c;其中b需要是列向量&#xff0c;用分号隔开元素&#xff1b; octave:7> A[1,2; 1.0001, 2;] A 1.0000 2.00001.0001 2.0000octave:8> b[3; 3.0001;] b 3.00003.0001octave:9> xA\b x 1.00001.0000octave:10> b-A*x ans 00octave:…

精通Nginx(03)-配置简述

本文主要讲述Nginx配置文件结构及调试技巧 使用nginx版本为1.24.0。 目录 Nginx目录 nginx.conf内容结构 配置片段化 配置调试技巧 Nginx目录 Nginx编译安装目录如下&#xff1a; 安装指定目录为"/usr/local"。配置目录为/usr/local/nginx/conf。 目录说明&am…