C++实现设计模式---命令模式 (Command)
命令模式 (Command)
命令模式 是一种行为型设计模式,它将请求封装为一个对象,从而使得可以用不同的请求对客户端进行参数化、对请求排队或记录日志,以及支持可撤销的操作。
意图
- 将操作的调用者与接收者分离,通过将请求封装为独立对象,使得请求更加灵活。
- 支持撤销、重做、记录日志等操作。
使用场景
- 需要参数化请求:
- 客户端不直接调用操作,而是通过封装的命令对象。
- 需要支持撤销 (Undo) 或重做 (Redo):
- 操作需要记录历史,以支持回滚或重试。
- 请求需要队列化:
- 系统需要对请求排队处理或记录日志。
参与者角色
- 命令接口 (Command)
- 定义所有命令的公共接口。
- 具体命令类 (ConcreteCommand)
- 实现命令接口,调用接收者执行具体操作。
- 接收者 (Receiver)
- 负责执行具体操作的对象。
- 调用者 (Invoker)
- 负责调用命令。
- 客户端 (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)
- 定义了命令的公共接口,所有具体命令都需要实现
execute和undo方法。
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撤销命令。
优缺点
优点
- 解耦调用者与接收者:
- 调用者无需知道接收者的具体实现。
- 支持撤销和重做:
- 通过记录命令历史,支持操作的撤销和重做。
- 命令队列化:
- 可以轻松实现请求的排队处理。
缺点
- 类数量增加:
- 每个操作都需要定义一个具体命令类。
- 存储开销:
- 需要存储命令历史以支持撤销和重做。
适用场景
- 参数化请求:
- 将请求封装为独立对象,客户端无需直接调用。
- 操作的撤销和重做:
- 系统需要支持操作回滚。
- 请求队列化:
- 系统需要对请求进行排队或记录日志。
总结
命令模式通过将请求封装为对象,实现了请求的参数化、撤销、排队处理等功能,是一种优雅的行为模式。适用于需要解耦调用者和接收者的场景,尤其在支持撤销或重做的系统中表现出色。
相关文章:
C++实现设计模式---命令模式 (Command)
命令模式 (Command) 命令模式 是一种行为型设计模式,它将请求封装为一个对象,从而使得可以用不同的请求对客户端进行参数化、对请求排队或记录日志,以及支持可撤销的操作。 意图 将操作的调用者与接收者分离,通过将请求封装为独…...
设计模式的艺术-享元模式
结构性模式的名称、定义、学习难度和使用频率如下表所示: 1.如何理解享元模式 当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题。 在享元模式中,存储这些共享实例对象的地方称为享元池&…...
Linux的权限和一些shell原理
目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型: 基本权限: chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后: 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…...
【Postgres_Python】使用python脚本批量创建和导入多个PG数据库
之前批量创建和导入数据库分为2个python脚本进行,现整合优化代码合并为一个python脚本,可同步实现数据库的创建和数据导入。之前的文章链接: 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…...
Ubuntu安装GitLab
在 Ubuntu 上安装 GitLab 的步骤如下。这里以 GitLab Community Edition(CE)为例: 前提条件 确保你的 Ubuntu 系统是 20.04 或更高版本。确保你的系统满足 GitLab 的硬件要求。 步骤 更新系统包: sudo apt update sudo apt upg…...
网络知识小科普--5
81、什么是组播路由? 组播路由是一种有针对性的广播形式,将消息发送到所选择的用户组,而不是将其发送到子网上的所有用户。 82、加密在网络上的重要性是什么? 加密是将信息转换成用户不可读的代码的过程。然后使用秘密密钥或密码将其翻译或解密回其…...
JavaScript学习记录23
第十一节 JSON对象 1. JSON 格式 JSON 格式(JavaScript Object Notation 的缩写)是一种用于数据交换的文本格式,2001年由 Douglas Crockford 提出,目的是取代繁琐笨重的 XML 格式。 相比 XML 格式,JSON 格式有两个显…...
VScode 开发 Springboot 程序
1. 通过maven创建springboot程序 输入 mvn archetype:generate 选择模板,一般默认选择为第 7 种方式; 选择之后,一般要你填写如下内容: groupId: 组织名称;artifactId: 项目名称;version: 版本࿰…...
.git/hooks/post-merge 文件的作用
.git/hooks/post-merge 文件是 Git 版本控制系统中的一个钩子(hook)脚本,其作用是在合并(merge)操作完成后自动执行一些特定的操作。以下是关于 .git/hooks/post-merge 文件作用的详细解释: 作用 自动化任…...
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 开发前端的详细过程: 一、创建项目 打开 Cursor 并新建项目: 启动 Cursor 编辑器。点击 “File” 菜单,选择 “New Project”。在弹出的对话框中,输入项目名称,如 “MyFrontendProject”࿰…...
基于微信小程序的移动学习平台的设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
atheris从安装到fuzz输入输出解读
1. 引入 模糊测试是一种自动化的软件测试技术,它通过自动生成大量随机数据作为输入来测试程序,以发现潜在的错误、漏洞或崩溃。atheris是一个专门用于CPython(Python的C语言实现)的模糊测试框架。 2. 安装atheris 参考1&#x…...
「 机器人 」系统辨识实验浅谈
前言 系统辨识实验是一种通过实验和数据分析的方法,用于建立物理系统的数学模型的技术。系统辨识是控制工程和系统科学中的重要环节,尤其是在模型未知或复杂的情况下。以下是系统辨识实验的详细介绍: 1. 系统辨识实验的目的 1.1 建模 为动态系统(如机械系统、电气系统或生…...
基于Flask的哔哩哔哩评论数据可视化分析系统的设计与实现
【Flask】基于Flask的哔哩哔哩评论数据可视化分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统可以搜索查看作者、播放量、评论等相关信息,并将相关的分析…...
[央企大赛 2025] pwn
拿到堆附件,不清楚哪个是密码哪个是pwn,找到两个pwn,一个RSA密码相对简单(已知e,d,N,直接用N解出k((ed-1)//phi_N(ed-1)//N^2),然后求pq,而phi_N正好是pq的2次方程)。就只复现了两个pwn,感觉还有…...
C语言初阶--折半查找算法
目录 练习1:在一个有序数组中查找具体的某个数字n 练习2:编写代码,演示多个字符从两端移动,向中间汇聚 练习3:简单编写代码实现,模拟用户登录情景,并且只能登录三次 练习4:猜数字…...
Python!从0开始学爬虫:(一)HTTP协议 及 请求与响应
前言 爬虫需要基础知识,HTTP协议只是个开始,除此之外还有很多,我们慢慢来记录。 今天的HTTP协议,会有助于我们更好的了解网络。 一、什么是HTTP协议 (1)定义 HTTP(超文本传输协议ÿ…...
[ 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…...
【项目初始化】自定义异常处理
我们在项目初始化的工作之一就是要自定义异常处理,用来处理项目中出现的各种异常,如业务异常、系统异常等等。 这些属于项目的通用基础代码,在任何后端中都可以复用。 1. 自定义错误码 自定义错误码,对错误进行收敛,…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
