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

news2025/1/4 7:31:29

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

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

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

相关文章

单元测试3.0+ @RunWith(JMockit.class)+mock+Expectations

Jmockit使用笔记_基本功能使用Tested_Injectable_Mocked_Expectations_jmockit.class-CSDN博客 测试框架Jmockit集合junit使用 RunWith(JMockit.class) 写在测试案例类上的注解 Tested 在测试案例中,写在我们要测试的类上面, 一般用实现类 Injectable 在测试案例中声明…

网络渗透测试实验二:网络嗅探与身份认证

1.实验目的和要求 1、通过使用Wireshark软件掌握Sniffer&#xff08;嗅探器&#xff09;工具的使用方法&#xff0c;实现捕捉HTTP等协议的数据包&#xff0c;以理解TCP/IP协议中多种协议的数据结构、通过实验了解HTTP等协议明文传输的特性。 2、研究交换环境下的网络嗅探实现…

mqtt连接onenet云平台

密码 version2018-10-31&resproducts%2FlzNd7drwE2%2Fdevices%2Flocation_1&et1756617761&methodmd5&sign52jsIUhK7i2zXjlEtkwDhQ%3D%3D 设备名称&#xff1a;temperatureAndHumidity 设备密钥&#xff1a;bE5OSHBlTHU3TDNSUUVoTmY0WWZqbThDVzNjOUJ3Y1Y 产品i…

Neo4j GDS 2.0 安装与配置

Neo4j GDS 2.0 安装与配置 GDS插件安装&#xff1a;Neo4j官方文档 1. GDS简介 Neo4j Graph Data Science (GDS) 库作为 Neo4j Graph Database 的插件提供。该插件需要安装到数据库中并在 Neo4j 配置中列入白名单。有两种主要方法可以实现这一点&#xff0c;我们将在本章中详…

艾体宝方案丨全面提升API安全:AccuKnox 接口漏洞预防与修复

一、API 安全&#xff1a;现代企业的必修课 在现代技术生态中&#xff0c;应用程序编程接口&#xff08;API&#xff09;扮演着不可或缺的角色。从数据共享到跨平台集成&#xff0c;API 成为连接企业系统与外部服务的桥梁。然而&#xff0c;伴随云计算的普及与微服务架构的流行…

使用JMeter对Linux生产服务器进行压力测试

安装 JMeter wget https://downloads.apache.org/jmeter/binaries/apache-jmeter-5.4.1.tgz tar -xzf apache-jmeter-5.4.1.tgz cd apache-jmeter-5.4.1创建 JMeter 脚本 设置中文 选择Options—>Choose Language—>选择其他语言&#xff08;例如&#xff1a;Chinese&am…

位置编码--RPE

相对位置编码 (Relative Position Encoding, RPE) 1. 相对位置编码 相对位置编码是 Transformer 中的一种改进位置编码方式&#xff0c;它的主要目的是通过直接建模序列中元素之间的相对位置&#xff0c;而不是绝对位置&#xff0c;从而更好地捕捉序列元素之间的依赖关系&#…

《代码随想录》Day21打卡!

写在前面&#xff1a;祝大家新年快乐&#xff01;&#xff01;&#xff01;2025年快乐&#xff0c;2024年拜拜~~~ 《代码随想录》二叉树&#xff1a;修剪二叉搜索树 本题的完整题目如下&#xff1a; 本题的完整思路如下&#xff1a; 1.本题使用递归进行求解&#xff0c;所以分…

【mysql】linux安装mysql客户端

参考文章&#xff1a; MySQL系列之如何在Linux只安装客户端 linux下安装mysql客户端client MySQL Community Downloads 查看linux版本方法&#xff1a; lsb_release -a cat /proc/version下载文件&#xff1a; rpm -ivh mysql-community-*可以删除错误的包&#xff1a; RP…

HTML——26.像素单位

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>像素</title></head><body><!--像素&#xff1a;1.指设备屏幕上的一个点&#xff0c;单位px&#xff0c;如led屏上的小灯朱2.当屏幕分辨率固定时&…

一键闪测仪:MLCC尺寸测量解决方案

MLCC是电子行业中常用的陶瓷电容器&#xff0c;其尺寸影响物理占用空间、电气性能和可靠性等&#xff0c;因此MLCC尺寸管控对产品质量至关重要。 在此&#xff0c;小优博士给各位介绍MLCC的概况以及MLCC尺寸快速测量解决方案。 一、MLCC概述 MLCC&#xff08;Multi-layer Cer…

Spring API 接口加密/解密

API 接口加密/解密 为了安全性需要对接口的数据进行加密处理&#xff0c;不能明文暴露数据。为此应该对接口进行加密/解密处理&#xff0c;对于接口的行为&#xff0c;分别有&#xff1a; 入参&#xff0c;对传过来的加密参数解密。接口处理客户端提交的参数时候&#xff0c;…

学习threejs,导入pdb格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.PDBLoader pdb模型加…

Frontend - 分页(针对 python / Django )

目录 一、同个文件内&#xff08;方式一&#xff09; 1. 前端 html 2. 定义分页界面 3. 获取分页数据 4.后端根据前端分页需求&#xff0c;整理分页数据 5.显示情况 6. JsonResponse 相关知识 二、不同文件内依旧有效&#xff08;方式二&#xff0c;更优化&#xff09;…

[2474].第04节:Activiti官方画流程图方式

我的后端学习大纲 Activiti大纲 1.安装位置&#xff1a; 2.启动&#xff1a;

按照乘法分解10点结构

在行列可自由变换的平面上9点结构有1430个&#xff0c;10点结构有3908个。其中可被分解为2*5的有102个&#xff0c; 5a1*2a110a28 5a1*2a210a689 5a1*2a310a1722 5a2*2a110a172 5a2*2a210a1081 5a2*2a310a2006 5a3*2a110a275 5a3*2a210a1561 5a3*2a310a2381 5a4*2a110…

JVM实战—6.频繁YGC和频繁FGC的后果

大纲 1.JVM GC导致系统突然卡死无法访问 2.什么是Young GC什么是Full GC 3.Young GC、Old GC和Full GC的发生情况 4.频繁YGC的案例(G1解决大内存YGC过慢) 5.频繁FGC的案例(YGC存活对象S区放不下) 6.问题汇总 1.JVM GC导致系统突然卡死无法访问 (1)基于JVM运行的系统最怕…

word运行时错误‘-2147221164(80040154)’ 没有注册类的解决办法

目录 问题描述解决方案 问题描述 解决方案 打开C盘找到路径C:\Users\Administrator\AppData\Roaming\Microsoft\Word\STARTUP或者在everything中搜索“Microsoft\Word\STARTUP”删除NEWebWordAddin.dotm文件即可正确打开word。

微服务保护—Sentinel快速入门+微服务整合 示例: 黑马商城

1.微服务保护 微服务保护是确保微服务架构可靠、稳定和安全的策略与技术。 在可靠性上&#xff0c;限流是控制进入微服务的请求数量&#xff0c;防止流量过大导致服务崩溃。比如电商促销时对商品详情服务进行流量限制。熔断是当被调用的微服务故障过多或响应过慢时&#xff0c;…