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

C++ 设计模式:命令模式(Command Pattern)

链接:C++ 设计模式
链接:C++ 设计模式 - 访问者模式

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

1. 问题分析

在开发中,我们经常需要向某个对象发送请求,但我们希望请求的发送者和接收者解耦。我们还可能需要对请求进行排队、记录日志,甚至支持撤销操作。

命令模式通过将请求封装成一个独立的对象,使得请求的发送者和接收者解耦。每个命令对象都实现一个统一的接口,包含执行请求的方法。这样,我们可以用不同的命令对象对客户进行参数化,并且可以很容易地扩展新的命令。

2.实现步骤

  1. 定义命令接口(Command):声明执行请求的方法。
  2. 实现具体命令类(ConcreteCommand):实现命令接口,执行具体的请求。
  3. 定义接收者类(Receiver):包含执行具体请求的方法。
  4. 定义调用者类(Invoker):持有命令对象,并在某个时刻调用命令对象的执行方法。
  5. 客户端代码(Client):创建具体命令对象,并将其传递给调用者。

3.代码示例

以机器人示例。

3.1.定义命令接口

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

3.2.实现具体命令类

// 接收者类:机器人
class Robot {public:void moveForward() { std::cout << "Robot moves forward" << std::endl; }void moveBackward() { std::cout << "Robot moves backward" << std::endl; }void turnLeft() { std::cout << "Robot turns left" << std::endl; }void turnRight() { std::cout << "Robot turns right" << std::endl; }
};

3.3.定义接收者类

// 具体命令类:前进
class MoveForwardCommand : public Command {public:MoveForwardCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->moveForward(); }private:Robot* robot_;
};
// 具体命令类:后退
class MoveBackwardCommand : public Command {public:MoveBackwardCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->moveBackward(); }private:Robot* robot_;
};
// 具体命令类:左转
class TurnLeftCommand : public Command {public:TurnLeftCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->turnLeft(); }private:Robot* robot_;
};
// 具体命令类:右转
class TurnRightCommand : public Command {public:TurnRightCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->turnRight(); }private:Robot* robot_;
};

3.4.定义调用者类

// 调用者类:遥控器
class RemoteControl {public:void setCommand(Command* command) { command_ = command; }void pressButton() {if (command_) {command_->execute();}}private:Command* command_ = nullptr;
};

3.5.客户端代码

int main() {// 创建接收者对象Robot robot;// 创建具体命令对象MoveForwardCommand moveForwardCommand(&robot);MoveBackwardCommand moveBackwardCommand(&robot);TurnLeftCommand turnLeftCommand(&robot);TurnRightCommand turnRightCommand(&robot);// 创建调用者对象RemoteControl remoteControl;// 设置命令并按下按钮remoteControl.setCommand(&moveForwardCommand);remoteControl.pressButton();remoteControl.setCommand(&moveBackwardCommand);remoteControl.pressButton();remoteControl.setCommand(&turnLeftCommand);remoteControl.pressButton();remoteControl.setCommand(&turnRightCommand);remoteControl.pressButton();return 0;
}

4.C++函数对象

函数对象是一个重载了 operator() 的类,其实例可以像函数一样被调用。函数对象的主要目的是将行为封装到对象中,使得对象可以像函数一样被调用。函数对象强调的是行为的封装和灵活性。

4.1.定义函数对象类

// 函数对象类:前进
class MoveForward {public:MoveForward(Robot* robot) : robot_(robot) {}void operator()() { robot_->moveForward(); }private:Robot* robot_;
};
// 函数对象类:后退
class MoveBackward {public:MoveBackward(Robot* robot) : robot_(robot) {}void operator()() { robot_->moveBackward(); }private:Robot* robot_;
};

4.2.定义调用者类

// 调用者类:遥控器
class RemoteControl {public:void setCommand(std::function<void()> command) { command_ = command; }void pressButton() {if (command_) {command_();}}private:std::function<void()> command_;
};

4.3.客户端代码

int main() {// 创建接收者对象Robot robot;// 创建函数对象MoveForward moveForward(&robot);MoveBackward moveBackward(&robot);// 创建调用者对象RemoteControl remoteControl;// 设置命令并按下按钮remoteControl.setCommand(moveForward);remoteControl.pressButton();remoteControl.setCommand(moveBackward);remoteControl.pressButton();return 0;
}

5.命令模式与函数对象的对比

5.1. 相似点

  1. 封装行为:命令模式和函数对象都可以用于封装行为,使得行为可以像对象一样被传递和调用。
  2. 解耦:两者都可以实现请求的发送者和接收者的解耦。

5.2. 不同点

  1. 设计意图:
    • 命令模式:主要用于将请求封装成对象,从而支持请求的排队、记录日志、撤销和重做等操作。
    • 函数对象:主要用于将行为封装到对象中,使得对象可以像函数一样被调用,强调行为的灵活性和可组合性。
  2. 结构复杂度:
    • 命令模式:通常包含多个角色(命令、具体命令、调用者、接收者),结构较为复杂。
    • 函数对象:通常只需要一个包含 operator() 方法的类,结构较为简单。
  3. 使用场景:
    • 命令模式:适用于需要对请求进行排队、记录日志、支持撤销和重做等操作的场景。
    • 函数对象:适用于需要将行为封装到对象中,并像函数一样调用的场景。

命令模式和函数对象在C++中都可以用于封装行为,但它们在设计意图和使用场景上有所不同。命令模式主要用于将请求封装成对象,从而支持请求的排队、记录日志、撤销和重做等操作;而函数对象主要用于将行为封装到对象中,使得对象可以像函数一样被调用,强调行为的灵活性和可组合性。

相关文章:

C++ 设计模式:命令模式(Command Pattern)

链接&#xff1a;C 设计模式 链接&#xff1a;C 设计模式 - 访问者模式 命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式&#xff0c;它将请求封装成一个对象&#xff0c;从而使你可以用不同的请求对客户进行参数化&#xff0c;对请求排队或记录请求日志…...

安卓/system/bin下命令中文说明(AI)

ATFWD-daemon&#xff1a;AT指令转发守护进程&#xff0c;用于将AT指令从应用层转发到调制解调器。 PktRspTest&#xff1a;数据包响应测试工具。 StoreKeybox&#xff1a;存储密钥盒工具&#xff0c;用于安全地存储加密密钥。 WifiLogger_app&#xff1a;WiFi日志记录应用&…...

MATLAB程序转C# WPF,dll集成,混合编程

工作中遇到一个需求&#xff0c;有一部分算法的代码需要MATLAB来进行处理&#xff0c;而最后需要集成到C#中的wpf项目中去&#xff0c;选择灵活性更高的dll&#xff0c;去进行集成。&#xff08;可以简单理解为&#xff1a;将MATLAB的函数&#xff0c;变为C#中类的函数成员&…...

【SpringBoot3】Spring Boot 3.0 集成 Mybatis Plus

文章目录 一、什么是 Mybatis Plus 特性 二、Spring Boot 3.0 集成 Mybatis Plus三、Mybatis Plus 查询示例 1、普通查询2、分页查询 参考 一、什么是 Mybatis Plus MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只…...

nvidia_gpu_exporter 显卡监控

导入 grafana/dashboard.json https://github.com/utkuozdemir/nvidia_gpu_exporter/blob/master/grafana/dashboard.json参考 nvidia_gpu_exporter...

WebSocket 的封装使用

import { ElMessage } from "element-plus";// 全局WebSocket实例 let ws null; let isConnected false; let currentWsUrl ; // 用于存储当前的wsUrl let baseURL ws://XXX.com:8081;const initWebSocket (wsUrl, sendData) > {return new Prom…...

SqlSession的线程安全问题源码分析

&#x1f3ae; 作者主页&#xff1a;点击 &#x1f381; 完整专栏和代码&#xff1a;点击 &#x1f3e1; 博客主页&#xff1a;点击 文章目录 SqlSession 是线程安全的吗&#xff1f;为什么说是线程不安全的&#xff1f;事务管理问题 数据库连接的共享问题 一级缓存线程安全问题…...

Java 8 及经典面试题全解析

Java 是目前非常流行的编程语言之一&#xff0c;其强大的生态系统和丰富的功能使得它在企业级开发中占据重要地位。在面试中&#xff0c;Java 的基础知识、集合框架、多线程、JVM&#xff0c;以及 Java 8 的新特性是重点考查内容。本文将结合 Java 8 和经典知识点&#xff0c;为…...

MySQL:安装配置(完整教程)

这里写目录标题 一、MySQL 简介二、下载 MySQL三、安装 MySQL四、配置环境变量五、配置 MySQL5.1 初始化 MySQL5.2 启动 MySQL 服务 六、修改 MySQL 密码七、卸载 MySQL八、结语 一、MySQL 简介 MySQL 是一款广泛使用的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;…...

Java - 日志体系_Apache Commons Logging(JCL)日志接口库_桥接Logback 及 源码分析

文章目录 PreApache CommonsApache Commons ProperLogging &#xff08;Apache Commons Logging &#xff09; JCL 集成logbackPOM依赖配置文件 logback.xml使用 源码分析jcl-over-slf4j 的工作原理1. LogFactory 的实现2. SLF4JLogFactory 和 Log 的实例化过程3. SLF4JLog 和 …...

高性能网络框架--fstack

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 Fstack 是一个高性能的网络框架&#xff0c;主要用于构建高性能的网络应用程序&#xff0c;特别是在处理大量并发连接时。它基于 Linux 的 epoll 机制&#xff0c;使用了多线程和事件驱动的编程模型。以下是对 …...

Unity Mesh生成Cube

1. 配置一个Cube的每个面的数据 一共是6个面&#xff0c;每个面包含的数据包括4个顶点的相对顶点坐标&#xff08;Cube的中心为原点&#xff09;&#xff0c;法线方向&#xff0c;UV坐标&#xff0c;顶点渲染顺序&#xff0c;以及这个面用到的材质&#xff0c;因为这里是Top&am…...

2、pycharm常用快捷命令和配置【持续更新中】

1、常用快捷命令 Ctrl / 行注释/取消行注释 Ctrl Alt L 代码格式化 Ctrl Alt I 自动缩进 Tab / Shift Tab 缩进、不缩进当前行 Ctrl N 跳转到类 Ctrl 鼠标点击方法 可以跳转到方法所在的类 2、使用pip命令安装request库 命令&#xff1a;pip install requests 安装好了…...

Go语言方法和接收器类型详解

Go语言方法和接收器类型详解 1. 方法接收器类型 1.1 值接收器 值接收器方法不会改变接收器的状态&#xff0c;因为Go语言会在调用时复制接收器的值。因此&#xff0c;任何对接收器成员变量的修改都只会影响副本&#xff0c;而不会影响原始结构体实例。 type Person struct …...

Flutter:打包apk,详细图文介绍(一)

困扰了一天&#xff0c;终于能正常打包apk安装了&#xff0c;记录下打包的流程。建议参考我这篇文章时&#xff0c;同时看下官网的构建说明。 官网构建并发布 Android 应用详情 1、AS创建Flutter项目 2、cmd执行命令 生成一个sunluyi.jks的文件&#xff0c;可以自行把sunluyi替…...

Vue.js组件开发-实现动态切换菜单简单示例

在Vue.js中&#xff0c;实现动态切换菜单通过组件化开发和Vue的响应式数据绑定来实现。 示例&#xff1a; 展示如何创建一个可以动态切换菜单的Vue组件。 首先&#xff0c;需要定义一个Vue组件&#xff0c;该组件将包含菜单项和用于切换菜单的状态。 1. 创建Vue组件 <t…...

如何在 Ubuntu 22.04 上优化 Apache 以应对高流量网站教程

简介 在本教程中&#xff0c;我们将学习如何优化 Apache 以应对高流量网站。 当运行高流量网站时&#xff0c;确保你的 Apache Web 服务器得到优化对于有效处理负载至关重要。在本指南中&#xff0c;我们将介绍配置 Apache 以提高性能和可扩展性的基本技巧。 为高流量网站优…...

17爬虫:关于DrissionPage相关内容的学习01

概述 前面我们已经大致了解了selenium的用法&#xff0c;DerssionPage同selenium一样&#xff0c;也是一个基于Python的网页自动化工具。 DrissionPage既可以实现网页的自动化操作&#xff0c;也能够实现收发数据包&#xff0c;也可以把两者的功能合二为一。 DressionPage的…...

【HarmonyOS之旅】HarmonyOS概述(一)

目录 1 -> HarmonyOS简介 2 -> HarmonyOS发展历程 3 -> HarmonyOS技术特性 3.1 -> 硬件互助&#xff0c;资源共享 3.1.1 -> 分布式软总线 3.1.2 -> 分布式设备虚拟化 3.1.3 -> 分布式数据管理 3.1.4 -> 分布式任务调度 3.1.5 -> 分布式连接…...

chatwoot 开源客服系统搭建

1. 准备开源客服系统&#xff08;我是用的Chatwoot &#xff09; 可以选择以下开源客服系统作为基础&#xff1a; Chatwoot: 开源&#xff0c;多语言&#xff0c;跟踪和分析&#xff0c;支持多渠道客户对接&#xff0c;自动化和工作流等。源码Zammad: 现代的开源工单系统。Fr…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...