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

21.命令模式(Command Pattern)

定义

命令模式(Command Pattern) 是一种行为型设计模式,它将请求封装成一个对象,从而使您可以使用不同的请求、队列、日志请求以及支持撤销操作等功能。命令模式通过将请求(命令)封装成对象,使得客户端可以将请求发送者与请求接收者解耦,从而更灵活地控制操作的执行。

特性

  • 命令对象:将请求封装成一个命令对象,该命令对象包含了执行的具体操作。
  • Invoker(调用者):调用命令对象的 execute() 方法来执行相应的操作。
  • Receiver(接收者):实际执行命令的对象。
  • Client(客户端):客户端创建命令对象并设置命令的接收者。
  • Command(命令接口):定义命令接口,声明执行操作的抽象方法。

命令模式使得我们能够通过不同的命令对象来执行不同的操作,且操作的执行由调用者控制。

场景

适用场景

  • 请求调用者与请求接收者解耦:当客户端希望通过发送请求来调用不同的操作,而不希望知道具体如何执行时,可以使用命令模式。
  • 需要参数化对象:当需要参数化对象进行命令的请求时,命令模式可以封装请求的参数。
  • 支持撤销和恢复操作:命令模式非常适合实现撤销和恢复操作,通过存储命令对象及其执行过程,能够轻松地实现撤销功能。
  • 支持队列或日志请求:命令模式可以将请求封装成对象,方便将多个命令排入队列或记录日志。

应用场景

  • 图形用户界面(GUI)中的按钮点击事件:通过命令模式将按钮的点击事件封装为命令,使得不同按钮的操作可以被独立控制。
  • 事务管理系统:在事务管理中,命令可以表示一系列操作,通过命令模式进行回滚或恢复。
  • 多操作处理系统:在系统中可能有多个操作(如编辑操作),使用命令模式可以统一管理操作。

类设计

命令模式通常包括以下几个角色:

  1. Command(命令接口):声明执行操作的抽象方法。
  2. ConcreteCommand(具体命令类):实现了 Command 接口,封装了具体的请求与接收者之间的关系。
  3. Receiver(接收者):负责执行与请求相关的操作。
  4. Invoker(调用者):调用命令对象来执行请求。
  5. Client(客户端):客户端创建命令对象并设置接收者。

代码实现

我们设计一个 遥控器操作 的例子。遥控器上有多个按钮,每个按钮对应一个操作(如打开电视、关闭空调等)。我们使用命令模式来封装每个按钮的操作,并通过遥控器(调用者)来执行这些操作。

1. 定义命令接口(Command)

#include <iostream>
#include <string>
using namespace std;// 命令接口
class Command {
public:virtual void execute() = 0;  // 执行命令的接口virtual ~Command() {}
};
  • Command 是命令接口,声明了 execute() 方法,该方法将由具体命令类来实现。

2. 定义具体命令类(ConcreteCommand)

// 电视打开命令
class TVOnCommand : public Command {
private:class TV* tv;  // 接收者(电视)public:TVOnCommand(class TV* tv) : tv(tv) {}void execute() override {tv->turnOn();  // 执行命令:打开电视}
};// 电视关闭命令
class TVOffCommand : public Command {
private:class TV* tv;public:TVOffCommand(class TV* tv) : tv(tv) {}void execute() override {tv->turnOff();  // 执行命令:关闭电视}
};// 空调开命令
class ACOnCommand : public Command {
private:class AC* ac;public:ACOnCommand(class AC* ac) : ac(ac) {}void execute() override {ac->turnOn();  // 执行命令:打开空调}
};// 空调关命令
class ACOffCommand : public Command {
private:class AC* ac;public:ACOffCommand(class AC* ac) : ac(ac) {}void execute() override {ac->turnOff();  // 执行命令:关闭空调}
};
  • 每个命令类(如 TVOnCommand、TVOffCommand 等)都实现了 Command 接口,并封装了具体的操作逻辑。
  • 每个命令类持有一个接收者(例如 TV 或 AC),并在 execute() 方法中调用接收者的方法执行具体的操作。

3. 定义接收者类(Receiver)

class TV {
public:void turnOn() {cout << "TV is turned ON." << endl;}void turnOff() {cout << "TV is turned OFF." << endl;}
};class AC {
public:void turnOn() {cout << "AC is turned ON." << endl;}void turnOff() {cout << "AC is turned OFF." << endl;}
};
  • TV 和 AC 类是接收者,包含了执行具体操作的方法(例如打开电视、关闭空调)。

4. 定义调用者类(Invoker)

class RemoteControl {
private:Command* command;  // 持有命令对象public:void setCommand(Command* command) {this->command = command;  // 设置命令对象}void pressButton() {command->execute();  // 执行命令}
};
  • RemoteControl 类是调用者,持有一个命令对象并在按下按钮时执行该命令。

5. 客户端调用

int main() {// 创建接收者对象TV* tv = new TV();AC* ac = new AC();// 创建命令对象Command* tvOn = new TVOnCommand(tv);Command* tvOff = new TVOffCommand(tv);Command* acOn = new ACOnCommand(ac);Command* acOff = new ACOffCommand(ac);// 创建遥控器RemoteControl* remote = new RemoteControl();// 按下按钮打开电视remote->setCommand(tvOn);remote->pressButton();// 按下按钮关闭电视remote->setCommand(tvOff);remote->pressButton();// 按下按钮打开空调remote->setCommand(acOn);remote->pressButton();// 按下按钮关闭空调remote->setCommand(acOff);remote->pressButton();// 清理内存delete tv;delete ac;delete tvOn;delete tvOff;delete acOn;delete acOff;delete remote;return 0;
}

6. 输出结果

TV is turned ON.
TV is turned OFF.
AC is turned ON.
AC is turned OFF.
  • 客户端通过 RemoteControl 类来控制设备的开关,每次按下按钮时,遥控器都会调用相应命令对象的 execute() 方法,来完成实际的操作。

命令模式的优缺点

优点

  • 解耦发送者和接收者:命令模式将请求的发送者和接收者解耦,客户端不需要知道谁会处理请求,只需要发送命令对象即可。
  • 支持撤销和恢复:命令模式可以很容易实现撤销和恢复操作,命令对象可以保存执行过程,支持回滚。
  • 命令队列和日志:命令可以存储在队列中或日志中,方便管理和回溯。
  • 可扩展性:新命令的增加不会影响现有代码,只需要新增具体命令类即可。

缺点

  • 类的数量增加:每个命令都会对应一个具体的命令类,可能会增加类的数量。
  • 系统结构复杂:使用命令模式时,系统中需要管理多个命令类、调用者和接收者,可能使系统结构变得复杂。

场景

适用场景:

  • GUI事件处理:例如,按钮点击、菜单选择等GUI事件的处理,可以通过命令模式将每个事件封装为命令对象。
  • 任务调度系统:将任务封装成命令对象,通过队列或调度器执行任务。
  • 撤销/恢复功能:如文本编辑器、绘图软件等,需要提供撤销和重做的功能,命令模式能很方便地实现该功能。
  • 宏命令:多个命令可以组合成一个“宏命令”,一起执行。

应用场景:

  1. 文本编辑器的撤销操作:用户进行文本编辑时,编辑操作可以封装为命令对象,撤销时,可以通过命令对象来恢复到之前的状态。
  2. 图形编辑器中的操作:在图形编辑器中,用户可以执行绘制、删除、移动等操作,每个操作都可以封装为命令对象,便于撤销和重做。
  3. 网络请求处理:网络请求可以封装为命令对象,每个请求可以通过命令对象来执行,便于管理请求的执行顺序和状态。

总结

命令模式通过将请求封装成命令对象,使得请求的发送者与接收者解耦。它可以帮助简化系统中的操作,支持撤销/恢复功能,并使得系统更具扩展性。命令模式适用于事件处理、任务调度、宏命令等场景,可以使系统的操作更加灵活和可管理。

相关文章:

21.命令模式(Command Pattern)

定义 命令模式&#xff08;Command Pattern&#xff09; 是一种行为型设计模式&#xff0c;它将请求封装成一个对象&#xff0c;从而使您可以使用不同的请求、队列、日志请求以及支持撤销操作等功能。命令模式通过将请求&#xff08;命令&#xff09;封装成对象&#xff0c;使…...

如何将本地 Node.js 服务部署到宝塔面板:完整的部署指南

文章简介&#xff1a; 将本地开发的 Node.js 项目部署到线上服务器是开发者常见的工作流程之一。在这篇文章中&#xff0c;我将详细介绍如何将本地的 Node.js 服务通过宝塔面板&#xff08;BT 面板&#xff09;上线。宝塔面板是一个强大的服务器管理工具&#xff0c;具有简洁的…...

4.3 线性回归的改进-岭回归/4.4分类算法-逻辑回归与二分类/ 4.5 模型保存和加载

4.3.1 带有L2正则化的线性回归-岭回归 岭回归&#xff0c;其实也是一种线性回归&#xff0c;只不过在算法建立回归方程的时候1&#xff0c;加上正则化的限制&#xff0c;从而达到解决过拟合的效果 4.3.1.1 API 4.3.1.2 观察正则化程度的变化&#xff0c;对结果的影响 正则化力…...

Mac 部署Ollama + OpenWebUI完全指南

文章目录 &#x1f4bb; 环境说明&#x1f6e0;️ Ollama安装配置1. 安装[Ollama](https://github.com/ollama/ollama)2. 启动Ollama3. 模型存储位置4. 配置 Ollama &#x1f310; OpenWebUI部署1. 安装Docker2. 部署[OpenWebUI](https://www.openwebui.com/)&#xff08;可视化…...

工业物联网平台-视频识别视频报警新功能正式上线

前言 视频监控作为中服云工业物联网平台4.0的功能已经上线运行。已为客户服务2年有余&#xff0c;为客户提供多路视频、实时在线监视和控制能力。服务客户实时发现现场、产线、设备出现随机故障、事故等&#xff0c;及时到场处理维修。 视频识别&视频报警新功能当前正式上…...

面试题 17.19. 消失的两个数字

文章目录 1.题目2.思路3.代码 1.题目 面试题 17.19. 消失的两个数字 给定一个数组&#xff0c;包含从 1 到 N 所有的整数&#xff0c;但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗&#xff1f; 以任意顺序返回这两个数字均可。 示例 1: **输入:** […...

Licode简介及与SRS对比

Licode 是一个开源的 WebRTC 通信框架,专注于多人实时音视频互动(如视频会议),而 SRS 是一个通用的 流媒体服务器,支持直播、低延迟流分发等场景。以下是两者的详细对比和 Licode 的核心解析: 一、Licode 核心解析 1. 定位与设计目标 核心功能:基于 WebRTC 的多人实时音…...

mysql的cpu使用率100%问题排查

背景 线上mysql服务器经常性出现cpu使用率100%的告警&#xff0c; 因此整理一下排查该问题的常规流程。 1. 确认CPU占用来源 检查系统进程 使用 top 或 htop 命令&#xff0c;确认是否是 mysqld 进程导致CPU满载&#xff1a;top -c -p $(pgrep mysqld)2. 实时分析MySQL活动 …...

Debian 安装 Nextcloud 使用 MariaDB 数据库 + Caddy + PHP-FPM

前言 之前通过 docker在ubuntu上安装Nextcloud&#xff0c;但是现在我使用PVE安装Debian虚拟机&#xff0c;不想通过docker安装了。下面开始折腾。 安装过程 步骤 1&#xff1a;更新系统并安装必要的软件 sudo apt update && sudo apt upgrade -y sudo apt install…...

qt6.8安装mysql8.0驱动

qt6.8安装mysql8.0驱动 qt6.8本身是不带mysql驱动。想要在qt里面使用mysql,还是比较麻烦的。需要自己编译驱动 首先下载qt源码&#xff0c;链接Index of /archive/qt/6.8/6.8.1/single 下载mysql对于驱动文件&#xff0c;链接是MySQL :: Download MySQL Connector/C (Archiv…...

π0开源了且推出自回归版π0-FAST——打造机器人动作专用的高效Tokenizer:比扩散π0的训练速度快5倍但效果相当

前言 过去的半个多月 对于大模型 deepseek火爆全球&#xff0c;我对其的解读也写成了整整一个系列 详见《火爆全球的DeepSeek系列模型》&#xff0c;涉及对GRPO、MLA、V3、R1的详尽细致深入的解读 某种意义来讲&#xff0c;deepseek 相当于把大模型的热度 又直接拉起来了——…...

今日AI和商界事件(2025-02-07)

今日AI领域的相关事件包括但不限于以下几个方面&#xff1a; 一、政策与监管 美国众议员推动禁止政府设备使用中国AI应用DeepSeek&#xff1a;美国众议院两名来自两党的议员提议立法&#xff0c;禁止联邦政府设备使用中国人工智能应用DeepSeek&#xff0c;理由是中国政府可能…...

【算法篇】贪心算法

目录 贪心算法 贪心算法实际应用 一&#xff0c;零钱找回问题 二&#xff0c;活动选择问题 三&#xff0c;分数背包问题 将数组和减半的最小操作次数 最大数 贪心算法 贪心算法&#xff0c;是一种在每一步选择中都采取当前状态下的最优策略&#xff0c;期望得到全局最优…...

《金字塔原理》笔记

金字塔原理一书的原理是关于结构化写作的&#xff0c;里面提出一个MECE法则&#xff1a;各个分论点之间要“相互独立、完全穷尽”。 我的总结 写作思路都是总分总。 要凝练最顶部的信息&#xff0c;然后按照三叉树&#xff08;最多四叉树&#xff09;一直分下去。 书中优雅的…...

蓝桥杯准备 【入门3】循环结构

素数小算法&#xff08;埃氏筛&&欧拉筛&#xff09; 以下四段代码都是求20以内的所有素数 1.0版求素数 #include<iostream> using namespace std;int main() {int n 20;for(int i2;i<n;i){int j0;for(j2;j<i;j)//遍历i{if(i%j0){break;}}if(ij){cout&l…...

MySQL三大日志——binlog、redoLog、undoLog详解

日志是mysql数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息&#xff0c;能帮助我们进行很多容错及分析工作&#xff0c;其中有三大日志与我们这些开发者息息相关&#xff0c;本文将介绍binlog、redoLog、undoLog三种日志&#xff1a; 1. redoLog 1.1 为什么…...

IDEA中Resolving Maven dependencies卡着不动解决方案

一、修改settings.xml Maven配置阿里云仓库主要通过修改Maven的settings.xml文件来实现‌。以下是具体步骤: ‌1、找到settings.xml文件‌: 通常位于Maven安装目录下的conf文件夹中,或者在用户目录下的.m2文件夹中(如果用户自定义了settings.xml的位置)。 2、‌编辑se…...

组合(力扣77)

从这道题开始&#xff0c;我们正式进入回溯算法的学习。之前在二叉树中只是接触到了一丢丢&#xff0c;而这里我们将使用回溯算法解决很多经典问题。 那么这道题是如何使用回溯算法的呢&#xff1f;在讲回溯之前&#xff0c;先说明一下此题是如何递归的。毕竟回溯递归不分家&a…...

X86中的常用寄存器

通用寄存器16个 RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP, R8, R9, R10, R11, R12, R13, R14, R15 其中&#xff1a; RAX&#xff1a;调用程序时&#xff0c;用于存储返回值。RCX&#xff1a;在字符串处理指令中&#xff0c;常用做计数器。RSI&#xff1a;在字符串处理指令中…...

SpringAI系列 - 使用LangGPT编写高质量的Prompt

目录 一、LangGPT —— 人人都可编写高质量 Prompt二、快速上手2.1 诗人 三、Role 模板3.1 Role 模板3.2 Role 模板使用步骤3.3 更多例子 四、高级用法4.1 变量4.2 命令4.3 Reminder4.4 条件语句4.5 Json or Yaml 方便程序开发 一、LangGPT —— 人人都可编写高质量 Prompt La…...

git撤销上一次的提交

1、撤销提交 如果需要撤销上一次的提交&#xff0c;只是提交到了本地&#xff0c;可以通过命令&#xff1a; // 撤销最近的提交&#xff08;保留修改&#xff09; git reset --soft HEAD~1 这个操作可以保留之前的提交和当前的修改。最近一次的提交到本地的修改的提交会回到…...

springboot+vue导入ruoyi项目的框架

一、介绍 RuoYi-Vue版本&#xff0c;采用了前后端分离的单体架构设计软件环境&#xff1a;JDK、Mysql、Redis、Maven、Node技术选型: Spring Boot、Spring Security、MyBatis、Jwt、Vue3、Element-Plus官方地址: https://gitee.com/y_project/RuoYi-Vue 官方推荐的版本如下&a…...

Conmi的正确答案——Rider中添加icon作为exe的图标

C#版本&#xff1a;.net 8.0 Rider版本&#xff1a;#RD-243.22562.250&#xff08;非商业使用版&#xff09; 1、添加图标到解决方案下&#xff1a; 2、打开“App.xaml”配置文件&#xff0c;添加配置&#xff1a; <Applicationx:Class"ComTransmit.App"xmlns&q…...

360手机刷机 360手机解Bootloader 360手机ROOT

360手机刷机 360手机解Bootloader 360手机ROOT 问&#xff1a;360手机已停产&#xff0c;现在和以后&#xff0c;能刷机吗&#xff1f; 答&#xff1a;360手机&#xff0c;是肯定能刷机的 360手机资源下载网站 360手机-360手机刷机RootTwrp 360os.top 360rom.github.io 一、…...

电风扇各国检测认证详细介绍美国FCC+UL欧盟CE+ROHS日本PSE+METI备案+英国UKCA

美国 &#xff1a; FCC认证 &#xff1a;产品进入美洲市场的通行证&#xff0c;需通过FCC SDOC认证。 FCC第15部分B: 该标准适用于非故意辐射设备&#xff0c;如家用电器、电脑设备等。它规定了这些设备在电磁环境中不会产生过多的辐射。 ​射频标准: FCC第15部分C:该标准适…...

实验3 词法分析(二)

实验3 词法分析(二) [实验目的]&#xff1a; 1 . 熟悉给定的词法分析程序&#xff1b; 2 . 改进词法分析程序。 [实验内容]&#xff1a; 1.尝试多方面改进TEST语言的文法&#xff0c;参考教材附录B词法分析程序TESTscan.c&#xff0c;在此词法分析程序的基础上改进程序&#x…...

VsCode创建VUE项目

1. 首先安装Node.js和npm 通过网盘分享的文件&#xff1a;vsCode和Node&#xff08;本人电脑Win11安装&#xff09; 链接: https://pan.baidu.com/s/151gBWTFZh9qIDS9XWMJVUA 提取码: 1234 它们是运行和构建Vue.js应用程序所必需的。 1.1 Node安装&#xff0c;点击下一步即可 …...

【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具04

SQLSERVER的ImpDp和ExpDp工具演示 1、指定某些表作为导出对象外 (-exclude_table) 验证用&#xff1a;导出的表&#xff0c;导入到新的数据库 2、指定某些表作为导出对象外 (-exclude_table) 支持模糊检索&#xff0c;可以使用星号 以s开头的表作为导出对象外&#xff0c;…...

国内知名Deepseek培训师培训讲师唐兴通老师讲授AI人工智能大模型实践应用

课程名称 《Deepseek人工智能大模型实践应用》 课程目标 全面了解Deepseek人工智能大模型的技术原理、功能特点及应用场景。 熟练掌握Deepseek大模型的提示词工程技巧&#xff0c;能够编写高质量的提示词。 掌握Deepseek大模型在办公、营销等领域的应用方法&#xff0c;提升…...

4.Python字符串和列表:字符串输入、字符串输出、下标和切片、字符串常见函数、列表(list)、列表的循环遍历、列表的增删改查、列表的嵌套、列表的切片

1. Python 字符串 1.1 字符串输入 input() 函数用于从用户获取字符串输入。它总是返回一个字符串类型的值。 # 从用户输入字符串 name input("请输入你的名字&#xff1a;") print(f"你好, {name}")1.2 字符串输出 字符串的输出通常使用 print() 函数…...