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

我们将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.rhkb.cn/news/177516.html

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

相关文章

【Leetcode】【消失的数字】【C语言】

方法一&#xff1a;按位异或&#xff08;找单身狗&#xff09; 我们知道&#xff1a;按位异或^操作原则&#xff1a;相同为零&#xff0c;相异为一 所以 0^aa a ^a0 a ^bb ^a int missingNumber(int* nums, int numsSize){ int i 0; int tem1 0,tem20; for (i 0;i < nu…

大厂面试题-介绍一下自己对Netty

目录 用三点来简单的介绍下Netty&#xff1a; 面试官&#xff1a;哦&#xff0c;还不错&#xff0c;那你在说说为什么要用Netty&#xff1f; 面试官&#xff1a;那你在通俗地说一下Netty可以做什么事情&#xff1f; 面试官&#xff1a;那&#xff0c;在说说Netty有几种线程…

XUbuntu22.04之simplenote支持的Markdown语法总结(一百九十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

linux下df -h 命令一直卡住的解决方法

在Linux中&#xff0c;偶尔遇到用 df -h 查看磁盘情况时&#xff0c;一直卡住无法显示结果。 解决方法&#xff1a; 1、首先使用strace追踪到底执行到哪里卡住 $ strace df -h 2、如果没有strace命令则进行安装 $ yum install strace -y 3、显示出卡住的地方&#xff0c;如…

SpringBoot源码透彻解析—bean生命周期

先跟一段debug再看总结&#xff1a; 1 创建实例 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation&#xff08;自定义一个对象或者代理对象&#xff09;createBeanInstance&#xff08;创建实例&#xff09;MergedBeanDefinitionPostProcessor.postProcess…

编程怎么学才高效?初学编程怎么样才容易入门?

学习编程并提高编程能力需要一种结构化的方法&#xff0c;其中包括理解基础概念、实践、反馈和持续学习。以下是一些高效学习编程的策略&#xff1a; 理解基础概念&#xff1a;在学习编程的初期&#xff0c;理解基础概念非常重要。这包括学习编程语言的基本语法、数据类型、控…

Java调用HTTPS接口,绕过SSL认证

1&#xff1a;说明 网络编程中&#xff0c;HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是一种通过加密的方式在计算机网络上进行安全通信的协议。网络传输协议&#xff0c;跟http相比更安全&#xff0c;因为他加上了SSL/TLS协议来加密通信内容。 Java调…

算法与数据结构-分治算法

文章目录 什么是分治算法分治算法应用举例分析分治思想在海量数据处理中的应用 什么是分治算法 分治算法&#xff08;divide and conquer&#xff09;的核心思想其实就是四个字&#xff0c;分而治之 &#xff0c;也就是将原问题划分成 n 个规模较小&#xff0c;并且结构与原问…

JavaEE入门介绍,HTTP协议介绍,常用状态码及含义,服务器介绍(软件服务器、云服务器)

一、JavaEE入门 JavaEE&#xff08;Java Enterprise Edition&#xff09;&#xff0c;Java企业版&#xff0c;是一个用于企业级web开发&#xff08;不需要使用控制台&#xff09;平台。最早由Sun公司定制并发布&#xff0c;后由Oracle负责维护。 JavaEE平台规范了在开发企业级w…

3D RPG Course | Core 学习日记三:Navigation智能导航地图烘焙

前言 前面我们已经绘制好了一个简单的地图场景&#xff0c;现在我们需要使用Navigation给地图做智能导航&#xff0c;以实现AI自动寻路&#xff0c;以及设置地图的可行走区域以及不可行走区域&#xff0c;Navigation的基础知识、原理、用法在Unity的官方文档&#xff0c;以及网…

cocos creator,vscode打开脚本报错,找不到cc模块问题

cocosCreator&#xff0c;用VSCODE打开写脚本代码的时候&#xff0c;会误报飘红&#xff0c;但实际上能正常运行。 我的版本是当前最新版本的3.8.1 解决方案: 在CocosCreator 的安装目录下 C:\ProgramData\cocos\editors\Creator\3.8.1\resources\resources\3d\engine\bin.dec…

人工智能AI 全栈体系(十一)

第一章 神经网络是如何实现的 这些神经网络越来越复杂&#xff0c;都是用BP算法求解。网络有些变化就可能需要重新推导&#xff0c;而在实验过程中可能会做很多尝试&#xff0c;这样每次都重新推导BP算法太麻烦了。 十、深度学习框架 现在有了很多深度学习框架&#xff0c;这…

OPENCV 闭运算实验示例代码morphologyEx()函数

void CrelaxMyFriendDlg::OnBnClickedOk() {hdc this->GetDC()->GetSafeHdc();// TODO: 在此添加控件通知处理程序代码string imAddr "c:/Users/actorsun/Pictures/";string imAddr1 imAddr"rice.png";Mat relax, positive;relax imread(imAddr1…

【机器学习】二、决策树

目录 一、决策树定义&#xff1a; 二、决策树特征选择 2.1 特征选择问题 2.2 信息增益 2.2.1 熵 2.2.2 信息增益 三、决策树的生成 3.1 ID3算法 3.1.1理论推导 3.1.2代码实现 3.2 C4.5 算法 3.2.1理论推导 ​ 3.2.2代码实现 四、决策树的剪枝 4.1 原理 4.2 算法思路&#xff1a…

css position属性与js滚动

“视口”就是浏览器窗口中实际显示文档内容的区域&#xff0c;不包含浏览器的“外框”&#xff0c;如菜单、工具条和标签。文档则是指整个网页。 1 css 的position static 正常定位&#xff0c;是元素position属性的默认值&#xff0c;元素遵循常规流。 relative 相对定位&…

如何卸载干净 IDEA(图文讲解)windows和Mac教程

大家好&#xff0c;我是sun~ 很多小伙伴会问 Windows / Mac 系统上要怎么彻底卸载 IDEA 呢&#xff1f; 本文通过图片文字&#xff0c;详细讲解具体步骤&#xff1a; 如何卸载干净 IDEA&#xff08;图文讲解&#xff09; Windows1、卸载 IDEA 程序2、注册表清理3、残留清理 M…

云安全-云原生基于容器漏洞的逃逸自动化手法(CDK check)

0x00 docker逃逸的方法种类 1、不安全的配置&#xff1a; 容器危险挂载&#xff08;挂载procfs&#xff0c;Scoket&#xff09; 特权模式启动的提权&#xff08;privileged&#xff09; 2、docker容器自身的漏洞 3、linux系统内核漏洞 这里参考Twiki的云安全博客&#xff0c;下…

查询平均提速 700%,奇安信基于 Apache Doris 升级日志安全分析系统

本文导读&#xff1a; 数智时代的到来使网络安全成为了不可忽视的重要领域。奇安信作为一家领先的网络安全解决方案领军者&#xff0c;致力于为企业提供先进全面的网络安全保护&#xff0c;其日志分析系统在网络安全中发挥着关键作用&#xff0c;通过对运行日志数据的深入分析…

Go的命令行工具开发:使用Cobra库

今天我们将深入探讨如何使用Go语言和Cobra库来开发命令行工具。 命令行工具在软件开发中有着广泛的应用&#xff0c;它们快速、高效&#xff0c;且易于自动化。 Go语言因其简洁、高效而被广泛用于命令行工具的开发。Cobra库则是Go中用于构建命令行工具的重要库之一。 为什么选…

C/C++ sizeof

介绍 sizeof 是一个关键字、操作符&#xff0c;也是一个编译时运算符 作用&#xff1a;返回一个对象或者类型所占的内存字节数 使用方法 sizeof(type_name);//sizeof(类型) sizeof(object);//sizeof(对象) 注意&#xff1a; sizeof 操作符不能用于函数类型&#xff0c;不…