C++ 命令模式详解
命令模式(Command Pattern)是一种行为设计模式,它将请求封装为对象,从而使你可以参数化客户端使用不同的请求、队列或日志请求,以及支持可撤销的操作。
核心概念
设计原则
命令模式遵循以下设计原则:
-
单一职责原则:将调用操作的对象与执行操作的对象分离
-
开闭原则:可以引入新命令而不修改现有代码
-
松耦合:解耦请求发送者和接收者
主要优点
-
解耦:分离请求的发送者和接收者
-
可扩展:容易添加新命令
-
可组合:可以组合多个命令
-
支持撤销:可以实现命令的撤销和重做
-
延迟执行:支持命令的队列和延迟执行
模式结构
主要组件
-
Command(命令接口)
-
声明执行操作的接口
-
-
ConcreteCommand(具体命令)
-
实现命令接口
-
定义接收者与动作之间的绑定关系
-
调用接收者的操作
-
-
Invoker(调用者)
-
要求命令执行请求
-
-
Receiver(接收者)
-
知道如何执行与请求相关的操作
-
-
Client(客户端)
-
创建具体命令并设置接收者
-
完整代码示例
#include <iostream>
#include <memory>
#include <vector>
#include <stack>
#include <string>// ==================== 接收者类 ====================
// 电灯 - 接收者1
class Light {std::string location_;public:explicit Light(const std::string& location) : location_(location) {}void on() {std::cout << location_ << " 电灯打开" << std::endl;}void off() {std::cout << location_ << " 电灯关闭" << std::endl;}
};// 风扇 - 接收者2
class Fan {enum { OFF, LOW, MEDIUM, HIGH } speed_;public:Fan() : speed_(OFF) {}void low() {speed_ = LOW;std::cout << "风扇设置为低速" << std::endl;}void medium() {speed_ = MEDIUM;std::cout << "风扇设置为中速" << std::endl;}void high() {speed_ = HIGH;std::cout << "风扇设置为高速" << std::endl;}void off() {speed_ = OFF;std::cout << "风扇关闭" << std::endl;}int getSpeed() const {return speed_;}
};// ==================== 命令接口 ====================
class Command {
public:virtual void execute() = 0;virtual void undo() = 0;virtual ~Command() = default;
};// ==================== 具体命令 ====================
// 电灯开命令
class LightOnCommand : public Command {Light& light_;public:explicit LightOnCommand(Light& light) : light_(light) {}void execute() override {light_.on();}void undo() override {light_.off();}
};// 电灯关命令
class LightOffCommand : public Command {Light& light_;public:explicit LightOffCommand(Light& light) : light_(light) {}void execute() override {light_.off();}void undo() override {light_.on();}
};// 风扇命令基类
class FanCommand : public Command {
protected:Fan& fan_;int prevSpeed_;public:explicit FanCommand(Fan& fan) : fan_(fan), prevSpeed_(fan.getSpeed()) {}void undo() override {switch (prevSpeed_) {case Fan::HIGH: fan_.high(); break;case Fan::MEDIUM: fan_.medium(); break;case Fan::LOW: fan_.low(); break;default: fan_.off(); break;}}
};// 风扇高速命令
class FanHighCommand : public FanCommand {
public:explicit FanHighCommand(Fan& fan) : FanCommand(fan) {}void execute() override {prevSpeed_ = fan_.getSpeed();fan_.high();}
};// 风扇关闭命令
class FanOffCommand : public FanCommand {
public:explicit FanOffCommand(Fan& fan) : FanCommand(fan) {}void execute() override {prevSpeed_ = fan_.getSpeed();fan_.off();}
};// 宏命令 - 命令组合
class MacroCommand : public Command {std::vector<std::unique_ptr<Command>> commands_;public:void addCommand(std::unique_ptr<Command> cmd) {commands_.push_back(std::move(cmd));}void execute() override {for (const auto& cmd : commands_) {cmd->execute();}}void undo() override {// 反向执行undofor (auto it = commands_.rbegin(); it != commands_.rend(); ++it) {(*it)->undo();}}
};// ==================== 调用者 ====================
class RemoteControl {std::vector<std::unique_ptr<Command>> onCommands_;std::vector<std::unique_ptr<Command>> offCommands_;std::stack<std::unique_ptr<Command>> undoStack_;public:RemoteControl() {// 初始化空命令auto noCommand = std::make_unique<Command>();for (int i = 0; i < 7; i++) {onCommands_.push_back(std::make_unique<NoCommand>());offCommands_.push_back(std::make_unique<NoCommand>());}}void setCommand(int slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) {onCommands_[slot] = std::move(onCmd);offCommands_[slot] = std::move(offCmd);}void onButtonWasPushed(int slot) {if (onCommands_[slot]) {onCommands_[slot]->execute();undoStack_.push(onCommands_[slot]->clone()); // 假设Command实现了clone方法}}void offButtonWasPushed(int slot) {if (offCommands_[slot]) {offCommands_[slot]->execute();undoStack_.push(offCommands_[slot]->clone());}}void undoButtonWasPushed() {if (!undoStack_.empty()) {undoStack_.top()->undo();undoStack_.pop();}}// 空命令类class NoCommand : public Command {public:void execute() override {}void undo() override {}};
};// ==================== 客户端代码 ====================
int main() {std::cout << "=== 命令模式演示: 智能家居遥控器 ===" << std::endl;// 创建接收者Light livingRoomLight("客厅");Light kitchenLight("厨房");Fan ceilingFan;// 创建命令auto livingRoomLightOn = std::make_unique<LightOnCommand>(livingRoomLight);auto livingRoomLightOff = std::make_unique<LightOffCommand>(livingRoomLight);auto kitchenLightOn = std::make_unique<LightOnCommand>(kitchenLight);auto kitchenLightOff = std::make_unique<LightOffCommand>(kitchenLight);auto ceilingFanHigh = std::make_unique<FanHighCommand>(ceilingFan);auto ceilingFanOff = std::make_unique<FanOffCommand>(ceilingFan);// 创建宏命令auto partyOn = std::make_unique<MacroCommand>();dynamic_cast<MacroCommand*>(partyOn.get())->addCommand(std::move(livingRoomLightOn));dynamic_cast<MacroCommand*>(partyOn.get())->addCommand(std::move(kitchenLightOn));dynamic_cast<MacroCommand*>(partyOn.get())->addCommand(std::move(ceilingFanHigh));auto partyOff = std::make_unique<MacroCommand>();dynamic_cast<MacroCommand*>(partyOff.get())->addCommand(std::move(livingRoomLightOff));dynamic_cast<MacroCommand*>(partyOff.get())->addCommand(std::move(kitchenLightOff));dynamic_cast<MacroCommand*>(partyOff.get())->addCommand(std::move(ceilingFanOff));// 设置遥控器RemoteControl remote;remote.setCommand(0, std::make_unique<LightOnCommand>(livingRoomLight), std::make_unique<LightOffCommand>(livingRoomLight));remote.setCommand(1, std::make_unique<LightOnCommand>(kitchenLight), std::make_unique<LightOffCommand>(kitchenLight));remote.setCommand(2, std::make_unique<FanHighCommand>(ceilingFan), std::make_unique<FanOffCommand>(ceilingFan));remote.setCommand(3, std::move(partyOn), std::move(partyOff));// 测试遥控器std::cout << "\n--- 测试单个命令 ---" << std::endl;remote.onButtonWasPushed(0); // 打开客厅灯remote.offButtonWasPushed(0); // 关闭客厅灯remote.undoButtonWasPushed(); // 撤销std::cout << "\n--- 测试风扇命令 ---" << std::endl;remote.onButtonWasPushed(2); // 风扇高速remote.offButtonWasPushed(2); // 风扇关闭remote.undoButtonWasPushed(); // 撤销std::cout << "\n--- 测试宏命令 ---" << std::endl;remote.onButtonWasPushed(3); // 派对模式开remote.offButtonWasPushed(3); // 派对模式关remote.undoButtonWasPushed(); // 撤销return 0;
}
模式变体
1. 支持重做的命令模式
class CommandHistory {std::stack<std::unique_ptr<Command>> undoStack_;std::stack<std::unique_ptr<Command>> redoStack_;public:void execute(std::unique_ptr<Command> cmd) {cmd->execute();undoStack_.push(std::move(cmd));// 执行新命令时清空重做栈while (!redoStack_.empty()) redoStack_.pop();}void undo() {if (!undoStack_.empty()) {auto cmd = std::move(undoStack_.top());undoStack_.pop();cmd->undo();redoStack_.push(std::move(cmd));}}void redo() {if (!redoStack_.empty()) {auto cmd = std::move(redoStack_.top());redoStack_.pop();cmd->execute();undoStack_.push(std::move(cmd));}}
};
2. 事务性命令
class TransactionalCommand : public Command {std::vector<std::unique_ptr<Command>> commands_;bool executed_ = false;public:void addCommand(std::unique_ptr<Command> cmd) {if (!executed_) {commands_.push_back(std::move(cmd));}}void execute() override {if (!executed_) {for (const auto& cmd : commands_) {cmd->execute();}executed_ = true;}}void undo() override {if (executed_) {for (auto it = commands_.rbegin(); it != commands_.rend(); ++it) {(*it)->undo();}executed_ = false;}}
};
实际应用场景
-
GUI操作:菜单项、按钮点击等操作的封装
-
事务系统:支持原子操作和回滚
-
游戏开发:游戏命令、回放系统
-
智能家居:设备控制命令
-
任务队列:异步任务执行和调度
相关文章:
C++ 命令模式详解
命令模式(Command Pattern)是一种行为设计模式,它将请求封装为对象,从而使你可以参数化客户端使用不同的请求、队列或日志请求,以及支持可撤销的操作。 核心概念 设计原则 命令模式遵循以下设计原则: 单…...

制作一款打飞机游戏47:跳转
编辑器的问题 我们开始为不同的敌人编写一些行为,到目前为止进展顺利,一切都很棒。但上次我们遇到了一些问题,我们发现在这个编辑器中编写代码有时有点困难,因为当你想要在某行之间插入内容时,你不得不删除一切然后重…...

本地部署ollama及deepseek(linux版)
一、安装ollama export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download"curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/download|$OLLAMA_MIRROR|g" | shexport OLLAMA_MIRROR&q…...
Java Spring Boot项目目录规范示例
以下是一个典型的 Java Spring Boot 项目目录结构规范示例,结合了分层架构和模块化设计的最佳实践: text 复制 下载 src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── myapp/ │…...
针对共享内存和上述windows消息机制 在C++ 和qt之间的案例 进行详细举例说明
针对共享内存和上述windows消息机制 在C++ 和qt之间的案例 进行详细举例说明 以下是关于在 C++ 和 Qt 中使用共享内存(QSharedMemory)和 Windows 消息机制(SendMessage / PostMessage)进行跨线程或跨进程通信的详细示例。 🧩 使用 QSharedMemory 进行进程间通信(Qt 示例…...

vue H5解决安卓手机软键盘弹出,页面高度被顶起
开发中安卓机上遇到的软键盘弹出导致布局问题 直接上代码_ 在这里插入代码片 <div class"container"><div class"appContainer" :style"{height:isKeyboardOpen? Heights :inherit}"><p class"name"><!-- 绑定…...

CSS专题之自定义属性
前言 石匠敲击石头的第 12 次 CSS 自定义属性是现代 CSS 的一个强大特性,可以说是前端开发需知、必会的知识点,本篇文章就来好好梳理一下,如果哪里写的有问题欢迎指出。 什么是 CSS 自定义属性 CSS 自定义属性英文全称是 CSS Custom Proper…...
问题 | 当前计算机视觉迫切解决的问题
当前计算机视觉领域虽然在技术上取得了显著进展,但仍面临一系列关键挑战。结合最新研究与应用现状,以下是最迫切需要解决的几大问题: 1. 数据质量与多样性不足 高质量标注数据的获取:训练高效模型依赖大量精准标注的数据&#x…...

七、深入 Hive DDL:管理表、分区与洞察元数据
作者:IvanCodes 日期:2025年5月13日 专栏:Hive教程 内容导航 一、表的 DDL 操作 (非创建)二、分区的 DDL 操作三、洞察元数据:SHOW 命令的威力结语:DDL 与 SHOW,Hive 管理的双翼练习题一、选择题二、代码题…...
Qt6.x检查网络是否在线(与Qt 5.x不同)
Qt 5.x.x 要判断客户端网络是否联通,一般用如下方法: #include <QNetworkConfigurationManager>auto netWorkCheck new QNetworkConfigurationManager(); auto flag netWorkCheck->isOnline(); Qt 6.x.x 废弃了 QNetworkConfigurationManag…...

直接在Excel中用Python Matplotlib/Seaborn/Plotly......
本次分享如何利用pyxll包,实现直接在Excel中使用Python Matplotlib/Seaborn/Plotly等强大可视化工具。 pyxll配置 pyxll安装 pip install pyxll pyxll install pyxll自定义方法 例如,自定义一个计算斐波那契数的方法fib,并使用pyxll装饰器…...

React面试常问问题详解
以下是30个React面试中常见的问题及简要解析,涵盖基础概念、核心原理、性能优化、Hooks、状态管理等方面,适用于初中高级开发者准备面试时参考: 一、React 基础与核心概念 React 是什么? React 是由 Facebook 开发的用于构建用户界…...

【Java】网络编程(Socket)
网络编程 Socket 我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则使用套接字Socket来进行分离 套接字就像是传输层为应用层开的一个小口,应用程…...

思科(Cisco ASA/Firepower)、华三(H3C)、华为(Huawei USG)防火墙 的基础配置
以下是针对 思科(Cisco ASA/Firepower)、华三(H3C)、华为(Huawei USG)防火墙 的基础配置指南,涵盖 区域划分、安全策略、NAT、路由 等核心功能。配置示例基于通用场景,实际部署时需根…...
华为海思系列----昇腾张量编译器(ATC)模型转换工具----入门级使用指南(LINUX版)
由于官方SDK比较冗余且经常跨文档讲解且SDK整理的乱七八糟,对于新手来说全部看完上手成本较高,本文旨在以简短的方式介绍 CAFFE / ONNX 模型转 om 模型,并进行推理的全流程。希望能够帮助到第一次接触华为海思框架的道友们。大佬们就没必要看这种基础文章啦! 注:本…...
supabase 怎么新建项目?
在 Supabase 中新建项目主要通过官方网站的仪表盘 (Dashboard) 来完成。以下是详细步骤: 通过 Supabase 仪表盘新建项目: 注册/登录 Supabase 账户: 访问 Supabase 官网:https://supabase.com/如果你还没有账户,点击 …...

Windows环境下maven的安装与配置
1.检查JAVA_HOME环境变量 Maven是使用java开发的,所以必须知道当前系统环境中的JDK的安装目录。 搜索栏直接输入“cmd” 或者 WinR 输入cmd 在打开的终端窗口输入“echo %JAVA_HOME”,就可以看到jdk的位置了。 如果没有的话,请参考我的文章&a…...

LeetCode:513、找树左下角的值
//递归法 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* t…...

Vxe UI vue vxe-table 实现表格数据分组功能,不是使用树结构,直接数据分组
Vxe UI vue vxe-table 实现表格数据分组功能,不是使用树结构,直接数据分组 查看官网:https://vxetable.cn gitbub:https://github.com/x-extends/vxe-table gitee:https://gitee.com/x-extends/vxe-table 代码 通过…...

如何禁止chrome自动更新
百度了一下 下面这个方法实测有效 目录 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable 3、验证是否禁止更新成功: 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable GoogleUpdater InternalService…...

阳光学院【2020下】计算机网络原理-A卷-试卷-期末考试试卷
一、单选题(共25分,每空1分) 1.ICMP协议工作在TCP/IP参考模型的 ( ) A.主机-网络 B.网络互联层 C.传输层 D.应用层 2.下列关于交换技术的说法中,错误的是 ( ) A.电路交换适用于突发式通信 B.报文交换不能满足实时通信 C.报文…...
Spring Boot 使用 OSHI 实现系统运行状态监控接口
在实际开发中,我们经常需要获取服务器的运行状态,例如:CPU 使用率、内存使用情况、磁盘状态、JVM 运行信息等,以便于运维监控和性能分析。本文将基于 Spring Boot OSHI 实现一个系统信息接口,可返回当前服务运行的详细…...
FastAPI+MongoDB+React实现查询博客详情功能
第一部分:FastAPI 和 MongoDB 后端 确保你的 FastAPI 应用已经配置好,并且 MongoDB 数据库已经运行。以下是完整的后端代码: # main.py from fastapi import FastAPI, HTTPException, Depends from motor.motor_asyncio import AsyncIOMotorClient from pydantic import B…...

kotlin-协程(什么是一个协程)
1.什么指一个协程对于线程来说一个thread就是就是指一个线程,thread为什么成为线程呢?因为他实现了对线程的一个抽象管理,可以管理这个线程,启动,可以查看各种信息 那么协程呢? public fun CoroutineScop…...

数组和切片的区别
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...

WPF内嵌其他进程的窗口
WPF内嵌其他进程窗口的常见方法有 HwndHost SetParent 和 WindowsFormsHost WinForms Panel SetParent 推荐使用自定义HwndHost 两者的对比区别 示例代码 public class MyWndHost : HwndHost {const int WS_CHILD 0x40000000;const int WS_VISIBLE 0x10000000;const i…...
阿里云购买ECS 安装redis mysql nginx jdk 部署jar 部署web
服务:ECS防火墙要开启、阿里云控制平台:网路端口安全策略要设置 阿里云服务维护 1.安装JDK 查询要安装jdk的版本,命令:yum -y list java* 命令:yum install -y java-1.8.0-openjdk.x86_64 yum install -y java-17-openjdk.x8…...

CVPR2025 | Prompt-CAM: 让视觉 Transformer 可解释以进行细粒度分析
Prompt-CAM: Making Vision Transformers Interpretable for Fine-Grained Analysis 摘要-Abstract引言-Introduction方法-Approach预备知识-PreliminariesPrompt-CAM: Prompt Class Attention Map特征识别与定位-Trait Identification and Localization变体与扩展-Variants an…...
Fabric系列 - SoftHSM 软件模拟HSM
在 fabric-ca-server 上使用软件模拟的 HSM(密码卡) 功能 安装 SoftHSMv2 教程 SoftHSMv2 默认的配置文件 /etc/softhsm2.conf默认的token目录 /var/lib/softhsm/tokens/ 初始化和启动fabric-ca-server,需要设置一个管理员用户的名称和密码 初始化令牌 # 初始…...

解锁 DevOps 新境界 :使用 Flux 进行 GitOps 现场演示 – 自动化您的 Kubernetes 部署
前言 GitOps 是实现持续部署的云原生方式。它的名字来源于标准且占主导地位的版本控制系统 Git。GitOps 的 Git 在某种程度上类似于 Kubernetes 的 etcd,但更进一步,因为 etcd 本身不保存版本历史记录。毋庸置疑,任何源代码管理服务…...