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

C++实现设计模式---命令模式 (Command)

命令模式 (Command)

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


意图
  • 将操作的调用者与接收者分离,通过将请求封装为独立对象,使得请求更加灵活。
  • 支持撤销、重做、记录日志等操作。

使用场景
  1. 需要参数化请求
    • 客户端不直接调用操作,而是通过封装的命令对象。
  2. 需要支持撤销 (Undo) 或重做 (Redo)
    • 操作需要记录历史,以支持回滚或重试。
  3. 请求需要队列化
    • 系统需要对请求排队处理或记录日志。

参与者角色
  1. 命令接口 (Command)
    • 定义所有命令的公共接口。
  2. 具体命令类 (ConcreteCommand)
    • 实现命令接口,调用接收者执行具体操作。
  3. 接收者 (Receiver)
    • 负责执行具体操作的对象。
  4. 调用者 (Invoker)
    • 负责调用命令。
  5. 客户端 (Client)
    • 创建命令对象,并将其传递给调用者。

示例代码

以下代码展示了命令模式的实现,模拟智能家居系统控制灯光的打开、关闭操作,并支持撤销功能。

#include <iostream>
#include <memory>
#include <stack>
#include <string>// 命令接口:定义命令的公共接口
class Command {
public:virtual ~Command() = default;// 执行命令virtual void execute() = 0;// 撤销命令virtual void undo() = 0;
};// 接收者:灯
class Light {
private:std::string name; // 灯的名称public:explicit Light(std::string name) : name(std::move(name)) {}void turnOn() {std::cout << name << " 灯已打开。
";}void turnOff() {std::cout << name << " 灯已关闭。
";}
};// 具体命令类:打开灯的命令
class LightOnCommand : public Command {
private:Light& light; // 具体接收者:灯public:explicit LightOnCommand(Light& light) : light(light) {}void execute() override {light.turnOn(); // 打开灯}void undo() override {light.turnOff(); // 撤销,关闭灯}
};// 具体命令类:关闭灯的命令
class LightOffCommand : public Command {
private:Light& light; // 具体接收者:灯public:explicit LightOffCommand(Light& light) : light(light) {}void execute() override {light.turnOff(); // 关闭灯}void undo() override {light.turnOn(); // 撤销,打开灯}
};// 调用者:遥控器
class RemoteControl {
private:std::stack<std::unique_ptr<Command>> commandHistory; // 存储命令历史public:void executeCommand(std::unique_ptr<Command> command) {command->execute(); // 执行命令commandHistory.push(std::move(command)); // 将命令存入历史}void undoLastCommand() {if (!commandHistory.empty()) {auto& lastCommand = commandHistory.top(); // 获取最近的命令lastCommand->undo(); // 撤销命令commandHistory.pop(); // 移除该命令} else {std::cout << "无可撤销的命令。
";}}
};// 客户端代码
int main() {Light livingRoomLight("客厅");Light bedroomLight("卧室");RemoteControl remoteControl;// 打开客厅灯remoteControl.executeCommand(std::make_unique<LightOnCommand>(livingRoomLight));// 关闭客厅灯remoteControl.executeCommand(std::make_unique<LightOffCommand>(livingRoomLight));// 打开卧室灯remoteControl.executeCommand(std::make_unique<LightOnCommand>(bedroomLight));// 撤销最近一次操作remoteControl.undoLastCommand();// 撤销最近一次操作remoteControl.undoLastCommand();return 0;
}

代码解析
1. 命令接口 (Command)
  • 定义了命令的公共接口,所有具体命令都需要实现 executeundo 方法。
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;
};
2. 接收者 (Light)
  • 实现灯的具体操作,包括 turnOn(打开灯)和 turnOff(关闭灯)。
  • 是命令的实际执行者。
class Light {
private:std::string name;
public:explicit Light(std::string name) : name(std::move(name)) {}void turnOn() { std::cout << name << " 灯已打开。
"; }void turnOff() { std::cout << name << " 灯已关闭。
"; }
};
3. 具体命令类
  • LightOnCommand
    • execute 方法中调用 turnOn 打开灯,在 undo 方法中调用 turnOff 撤销。
  • LightOffCommand
    • execute 方法中调用 turnOff 关闭灯,在 undo 方法中调用 turnOn 撤销。
class LightOnCommand : public Command {
private:Light& light;
public:explicit LightOnCommand(Light& light) : light(light) {}void execute() override { light.turnOn(); }void undo() override { light.turnOff(); }
};
4. 调用者 (RemoteControl)
  • RemoteControl 负责调用命令对象的 execute 方法。
  • 使用栈 (std::stack) 存储命令历史,以支持撤销。
class RemoteControl {
private:std::stack<std::unique_ptr<Command>> commandHistory;
public:void executeCommand(std::unique_ptr<Command> command) {command->execute();commandHistory.push(std::move(command));}void undoLastCommand() {if (!commandHistory.empty()) {commandHistory.top()->undo();commandHistory.pop();} else {std::cout << "无可撤销的命令。
";}}
};
5. 客户端
  • 客户端创建具体命令对象,并通过调用者 RemoteControl 执行命令。
  • 通过 undoLastCommand 撤销命令。

优缺点
优点
  1. 解耦调用者与接收者
    • 调用者无需知道接收者的具体实现。
  2. 支持撤销和重做
    • 通过记录命令历史,支持操作的撤销和重做。
  3. 命令队列化
    • 可以轻松实现请求的排队处理。
缺点
  1. 类数量增加
    • 每个操作都需要定义一个具体命令类。
  2. 存储开销
    • 需要存储命令历史以支持撤销和重做。

适用场景
  1. 参数化请求
    • 将请求封装为独立对象,客户端无需直接调用。
  2. 操作的撤销和重做
    • 系统需要支持操作回滚。
  3. 请求队列化
    • 系统需要对请求进行排队或记录日志。

总结

命令模式通过将请求封装为对象,实现了请求的参数化、撤销、排队处理等功能,是一种优雅的行为模式。适用于需要解耦调用者和接收者的场景,尤其在支持撤销或重做的系统中表现出色。

相关文章:

C++实现设计模式---命令模式 (Command)

命令模式 (Command) 命令模式 是一种行为型设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使得可以用不同的请求对客户端进行参数化、对请求排队或记录日志&#xff0c;以及支持可撤销的操作。 意图 将操作的调用者与接收者分离&#xff0c;通过将请求封装为独…...

设计模式的艺术-享元模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解享元模式 当一个软件系统在运行时产生的对象数量太多&#xff0c;将导致运行代价过高&#xff0c;带来系统性能下降等问题。 在享元模式中&#xff0c;存储这些共享实例对象的地方称为享元池&…...

Linux的权限和一些shell原理

目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型&#xff1a; 基本权限&#xff1a; chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后&#xff1a; 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…...

【Postgres_Python】使用python脚本批量创建和导入多个PG数据库

之前批量创建和导入数据库分为2个python脚本进行&#xff0c;现整合优化代码合并为一个python脚本&#xff0c;可同步实现数据库的创建和数据导入。之前的文章链接&#xff1a; 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…...

Ubuntu安装GitLab

在 Ubuntu 上安装 GitLab 的步骤如下。这里以 GitLab Community Edition&#xff08;CE&#xff09;为例&#xff1a; 前提条件 确保你的 Ubuntu 系统是 20.04 或更高版本。确保你的系统满足 GitLab 的硬件要求。 步骤 更新系统包&#xff1a; sudo apt update sudo apt upg…...

网络知识小科普--5

81、什么是组播路由? 组播路由是一种有针对性的广播形式&#xff0c;将消息发送到所选择的用户组&#xff0c;而不是将其发送到子网上的所有用户。 82、加密在网络上的重要性是什么? 加密是将信息转换成用户不可读的代码的过程。然后使用秘密密钥或密码将其翻译或解密回其…...

JavaScript学习记录23

第十一节 JSON对象 1. JSON 格式 JSON 格式&#xff08;JavaScript Object Notation 的缩写&#xff09;是一种用于数据交换的文本格式&#xff0c;2001年由 Douglas Crockford 提出&#xff0c;目的是取代繁琐笨重的 XML 格式。 相比 XML 格式&#xff0c;JSON 格式有两个显…...

VScode 开发 Springboot 程序

1. 通过maven创建springboot程序 输入 mvn archetype:generate 选择模板&#xff0c;一般默认选择为第 7 种方式&#xff1b; 选择之后&#xff0c;一般要你填写如下内容&#xff1a; groupId: 组织名称&#xff1b;artifactId: 项目名称&#xff1b;version: 版本&#xff0…...

.git/hooks/post-merge 文件的作用

.git/hooks/post-merge 文件是 Git 版本控制系统中的一个钩子&#xff08;hook&#xff09;脚本&#xff0c;其作用是在合并&#xff08;merge&#xff09;操作完成后自动执行一些特定的操作。以下是关于 .git/hooks/post-merge 文件作用的详细解释&#xff1a; 作用 自动化任…...

Kafak 单例生产者实现-C#操作

前面写了一篇入门操作的文章,因为工作需要,简单修改了下如何实现单例生产者。 Kafka入门-C#操作_c# kafka-CSDN博客文章浏览阅读1.6k次,点赞20次,收藏9次。2).报错:“kafka.zookeeper.ZooKeeperClientTimeoutException: Timed out waiting for connection while in state…...

Cursor开发前端的详细过程

以下是使用 Cursor 开发前端的详细过程&#xff1a; 一、创建项目 打开 Cursor 并新建项目&#xff1a; 启动 Cursor 编辑器。点击 “File” 菜单&#xff0c;选择 “New Project”。在弹出的对话框中&#xff0c;输入项目名称&#xff0c;如 “MyFrontendProject”&#xff0…...

基于微信小程序的移动学习平台的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

atheris从安装到fuzz输入输出解读

1. 引入 模糊测试是一种自动化的软件测试技术&#xff0c;它通过自动生成大量随机数据作为输入来测试程序&#xff0c;以发现潜在的错误、漏洞或崩溃。atheris是一个专门用于CPython&#xff08;Python的C语言实现&#xff09;的模糊测试框架。 2. 安装atheris 参考1&#x…...

「 机器人 」系统辨识实验浅谈

前言 系统辨识实验是一种通过实验和数据分析的方法,用于建立物理系统的数学模型的技术。系统辨识是控制工程和系统科学中的重要环节,尤其是在模型未知或复杂的情况下。以下是系统辨识实验的详细介绍: 1. 系统辨识实验的目的 1.1 建模 为动态系统(如机械系统、电气系统或生…...

基于Flask的哔哩哔哩评论数据可视化分析系统的设计与实现

【Flask】基于Flask的哔哩哔哩评论数据可视化分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统可以搜索查看作者、播放量、评论等相关信息&#xff0c;并将相关的分析…...

[央企大赛 2025] pwn

拿到堆附件&#xff0c;不清楚哪个是密码哪个是pwn&#xff0c;找到两个pwn&#xff0c;一个RSA密码相对简单&#xff08;已知e,d,N,直接用N解出k((ed-1)//phi_N(ed-1)//N^2),然后求pq&#xff0c;而phi_N正好是pq的2次方程&#xff09;。就只复现了两个pwn&#xff0c;感觉还有…...

C语言初阶--折半查找算法

目录 练习1&#xff1a;在一个有序数组中查找具体的某个数字n 练习2&#xff1a;编写代码&#xff0c;演示多个字符从两端移动&#xff0c;向中间汇聚 练习3&#xff1a;简单编写代码实现&#xff0c;模拟用户登录情景&#xff0c;并且只能登录三次 练习4&#xff1a;猜数字…...

Python!从0开始学爬虫:(一)HTTP协议 及 请求与响应

前言 爬虫需要基础知识&#xff0c;HTTP协议只是个开始&#xff0c;除此之外还有很多&#xff0c;我们慢慢来记录。 今天的HTTP协议&#xff0c;会有助于我们更好的了解网络。 一、什么是HTTP协议 &#xff08;1&#xff09;定义 HTTP&#xff08;超文本传输协议&#xff…...

[ Spring ] Spring Cloud Gateway 2025 Comprehensive Overview

文章目录 Spring Gateway ArchitectureProject Level DependencyService CenterService ProviderGateway ServiceLaunch All Service Spring Gateway Architecture Service Center : register and find service providerService Provider : programs that provide actual serv…...

【项目初始化】自定义异常处理

我们在项目初始化的工作之一就是要自定义异常处理&#xff0c;用来处理项目中出现的各种异常&#xff0c;如业务异常、系统异常等等。 这些属于项目的通用基础代码&#xff0c;在任何后端中都可以复用。 1. 自定义错误码 自定义错误码&#xff0c;对错误进行收敛&#xff0c;…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...