【C++】命令模式

目录

  • 一、模式核心概念与结构
  • 二、C++ 实现示例:遥控器与家电控制
  • 三、命令模式的关键特性
  • 四、应用场景
  • 五、命令模式与其他设计模式的关系
  • 六、C++ 标准库中的命令模式应用
  • 七、优缺点分析
  • 八、实战案例:数据库事务命令
  • 九、实现注意事项
    • 如果这篇文章对你有所帮助,渴望获得你的一个点赞!

命令模式(Command Pattern)是一种【行为型】设计模式,它将请求封装为对象,从而使你可以用不同的请求对客户端进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。这种模式将发起请求的对象(客户端)与执行请求的对象(接收者)解耦,通过命令对象作为中间层来协调两者。

一、模式核心概念与结构

命令模式包含四个核心角色:

  1. 命令接口(Command):定义执行操作的接口,通常包含execute()方法。
  2. 具体命令(Concrete Command):实现命令接口,持有接收者的引用,并调用接收者的相应方法。
  3. 接收者(Receiver):知道如何执行与请求相关的操作,负责具体业务逻辑。
  4. 调用者(Invoker):持有命令对象并触发执行,不直接与接收者交互。

二、C++ 实现示例:遥控器与家电控制

以下是一个经典的命令模式示例,演示如何用命令模式实现家电的远程控制:

#include <iostream>
#include <string>
#include <memory>
#include <vector>// 接收者:家电
class Light {
public:void turnOn() { std::cout << "Light is on" << std::endl; }void turnOff() { std::cout << "Light is off" << std::endl; }
};class TV {
public:void turnOn() { std::cout << "TV is on" << std::endl; }void turnOff() { std::cout << "TV is off" << std::endl; }void setChannel(int channel) {std::cout << "TV channel set to " << channel << std::endl;}
};// 命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;  // 支持撤销操作
};// 具体命令:开灯命令
class LightOnCommand : public Command {
private:std::shared_ptr<Light> light;public:LightOnCommand(std::shared_ptr<Light> l) : light(l) {}void execute() override {light->turnOn();}void undo() override {light->turnOff();}
};// 具体命令:关灯命令
class LightOffCommand : public Command {
private:std::shared_ptr<Light> light;public:LightOffCommand(std::shared_ptr<Light> l) : light(l) {}void execute() override {light->turnOff();}void undo() override {light->turnOn();}
};// 具体命令:电视命令
class TVOnCommand : public Command {
private:std::shared_ptr<TV> tv;public:TVOnCommand(std::shared_ptr<TV> t) : tv(t) {}void execute() override {tv->turnOn();tv->setChannel(1);  // 默认频道}void undo() override {tv->turnOff();}
};// 调用者:遥控器
class RemoteControl {
private:std::shared_ptr<Command> onCommand;std::shared_ptr<Command> offCommand;std::vector<std::shared_ptr<Command>> history;  // 命令历史,用于撤销public:void setCommands(std::shared_ptr<Command> on, std::shared_ptr<Command> off) {onCommand = on;offCommand = off;}void pressOnButton() {if (onCommand) {onCommand->execute();history.push_back(onCommand);}}void pressOffButton() {if (offCommand) {offCommand->execute();history.push_back(offCommand);}}void pressUndoButton() {if (!history.empty()) {auto lastCommand = history.back();lastCommand->undo();history.pop_back();std::cout << "Undo last command" << std::endl;} else {std::cout << "Nothing to undo" << std::endl;}}
};// 客户端代码
int main() {// 创建接收者auto livingRoomLight = std::make_shared<Light>();auto tv = std::make_shared<TV>();// 创建具体命令auto lightOn = std::make_shared<LightOnCommand>(livingRoomLight);auto lightOff = std::make_shared<LightOffCommand>(livingRoomLight);auto tvOn = std::make_shared<TVOnCommand>(tv);// 创建调用者RemoteControl remote;// 设置命令remote.setCommands(lightOn, lightOff);// 使用遥控器std::cout << "=== Control Light ===" << std::endl;remote.pressOnButton();    // 开灯remote.pressOffButton();   // 关灯remote.pressUndoButton();  // 撤销:重新开灯// 切换控制对象remote.setCommands(tvOn, lightOff);std::cout << "\n=== Control TV ===" << std::endl;remote.pressOnButton();    // 开电视remote.pressUndoButton();  // 撤销:关电视return 0;
}

三、命令模式的关键特性

  1. 请求封装
    • 将请求(操作)封装为命令对象,使请求可以被存储、传递和调用。
  2. 解耦调用者和接收者
    • 调用者(如遥控器)无需知道接收者(如家电)的具体实现,只需调用命令接口。
  3. 支持撤销和重做
    • 通过在命令接口中定义undo()方法,可以实现操作的撤销功能。
  4. 队列和日志
    • 命令对象可以被序列化和存储,支持请求的排队、记录和回放。

四、应用场景

  1. 撤销 / 重做系统
    • 文本编辑器、图形设计工具中的撤销操作。
    • 数据库事务的回滚机制。
  2. 异步任务执行
    • 线程池、消息队列中的任务调度。
    • 网络请求的异步处理。
  3. 菜单和按钮系统
    • GUI 应用中的菜单项和按钮操作。
    • 游戏中的动作命令(如攻击、跳跃)。
  4. 宏录制
    • 办公软件中的宏功能,记录一系列操作并回放。
  5. 分布式系统
    • 远程过程调用(RPC)中的命令传递。

五、命令模式与其他设计模式的关系

  1. 备忘录模式
    • 命令模式实现操作的撤销,备忘录模式保存对象的状态。
    • 两者可结合使用,命令模式负责执行操作,备忘录模式提供状态恢复。
  2. 观察者模式
    • 命令模式用于处理请求,观察者模式用于事件通知。
    • 命令的执行可以触发观察者模式中的事件。
  3. 责任链模式
    • 命令模式将请求封装为对象,责任链模式将请求传递给多个处理者。
    • 两者可结合使用,责任链中的处理者可以是命令对象。

六、C++ 标准库中的命令模式应用

  1. 函数对象(Functor)
    • std::function和函数指针可视为轻量级命令模式实现。
    • 例如,std::sort接受一个比较函数对象作为参数。
  2. 线程池
    • C++11 的std::threadstd::async可结合命令模式实现任务队列。
  3. 信号与槽机制
    • Qt 框架中的信号与槽是命令模式的典型应用,信号触发时执行槽函数。

七、优缺点分析

优点:

  • 解耦调用者和接收者:降低系统耦合度,提高可维护性。
  • 支持扩展性:易于添加新的命令类,符合开闭原则。
  • 支持撤销和日志:方便实现复杂的操作管理功能。

缺点:

  • 类数量增加:每个具体命令都需要一个类,可能导致类膨胀。
  • 实现复杂度:对于简单操作,使用命令模式可能显得冗余。
  • 性能开销:封装请求为对象会引入额外的间接性,可能影响性能。

八、实战案例:数据库事务命令

以下是一个数据库事务命令的实现示例,支持事务的提交和回滚:

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <stack>// 接收者:数据库连接
class Database {
public:void executeQuery(const std::string& query) {std::cout << "Executing query: " << query << std::endl;// 实际执行SQL查询...}void commit() {std::cout << "Committing transaction" << std::endl;// 提交事务...}void rollback() {std::cout << "Rolling back transaction" << std::endl;// 回滚事务...}
};// 命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;
};// 具体命令:SQL查询命令
class QueryCommand : public Command {
private:std::shared_ptr<Database> database;std::string query;bool isTransactional;public:QueryCommand(std::shared_ptr<Database> db, const std::string& q, bool transactional = false): database(db), query(q), isTransactional(transactional) {}void execute() override {database->executeQuery(query);}void undo() override {if (isTransactional) {database->rollback();} else {std::cout << "Non-transactional query cannot be undone" << std::endl;}}
};// 具体命令:事务提交命令
class CommitCommand : public Command {
private:std::shared_ptr<Database> database;public:CommitCommand(std::shared_ptr<Database> db) : database(db) {}void execute() override {database->commit();}void undo() override {std::cout << "Commit cannot be undone" << std::endl;}
};// 调用者:事务管理器
class TransactionManager {
private:std::shared_ptr<Database> database;std::stack<std::shared_ptr<Command>> commandStack;  // 命令栈,用于撤销public:TransactionManager(std::shared_ptr<Database> db) : database(db) {}void executeCommand(std::shared_ptr<Command> command) {command->execute();commandStack.push(command);}void commit() {auto commitCmd = std::make_shared<CommitCommand>(database);commitCmd->execute();commandStack.push(commitCmd);}void rollbackLastCommand() {if (!commandStack.empty()) {auto lastCommand = commandStack.top();lastCommand->undo();commandStack.pop();std::cout << "Rolled back last command" << std::endl;} else {std::cout << "No commands to roll back" << std::endl;}}
};// 客户端代码
int main() {// 创建数据库连接auto db = std::make_shared<Database>();// 创建事务管理器TransactionManager txManager(db);// 执行一系列命令std::cout << "=== Executing commands ===" << std::endl;txManager.executeCommand(std::make_shared<QueryCommand>(db, "BEGIN TRANSACTION", true));txManager.executeCommand(std::make_shared<QueryCommand>(db, "INSERT INTO users VALUES ('Alice')"));txManager.executeCommand(std::make_shared<QueryCommand>(db, "INSERT INTO users VALUES ('Bob')"));// 回滚最后一条命令std::cout << "\n=== Rolling back last command ===" << std::endl;txManager.rollbackLastCommand();// 提交事务std::cout << "\n=== Committing transaction ===" << std::endl;txManager.commit();// 尝试回滚提交操作(不可行)std::cout << "\n=== Trying to roll back commit ===" << std::endl;txManager.rollbackLastCommand();return 0;
}

九、实现注意事项

  1. 撤销机制设计
    • 确保命令的undo()方法能正确恢复系统状态。
    • 对于某些不可撤销的操作(如删除文件),undo()可记录日志或提示用户。
  2. 命令参数化
    • 命令可以设计为接受参数,使其更加灵活(如SetChannelCommand可接受频道号)。
  3. 宏命令(Composite Command)
    • 可以创建包含多个命令的宏命令,实现批量操作。
  4. 线程安全
    • 在多线程环境中,命令的执行可能需要同步机制(如互斥锁)。

命令模式是 C++ 中实现请求封装和操作管理的重要工具,通过将请求转化为对象,使系统更加灵活、可扩展,并支持复杂的功能如撤销、队列和日志。


如果这篇文章对你有所帮助,渴望获得你的一个点赞!

在这里插入图片描述

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

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

相关文章

基于librdkafka开发的C++客户端,生产者生产发送数据失败问题处理

我们的项目使用了开源的librdkafka库&#xff0c;实现向kafka服务器生产发送数据的功能。使用的librdkafka的版本是1.9.0。 作为客户端程序&#xff0c;在开发时和客户协商确认后&#xff0c;支持了SASL_PLAINTEXT认证。以下概念解释引用自通义千问AI SASL (Simple Authentic…

OpenGL之yaw、pitch、fov 和 lookAt

在 3D 图形学中&#xff0c;yaw、pitch、fov 和 lookAt 都是控制摄像机&#xff08;Camera&#xff09;行为的关键参数&#xff0c;但它们的 作用层级 和 使用场景 不同。 1. yaw、pitch、fov 的作用 (1) yaw&#xff08;偏航角&#xff09; 作用&#xff1a;控制摄像机 左右…

STM32-第一节-新建工程,GPIO,点亮LED,蜂鸣器

一、新建工程&#xff1a; 1.Keil中新建工程&#xff0c;选择开发板型号。 2.工程文件夹建立Start&#xff0c;Library等分类&#xff0c;复制模版工程中的文件到工程文件夹中。 3.在Keil中添加分组&#xff0c;添加文件。 4.工程选项设置&#xff1a; c/c中&#xff1a;Inc…

Rust标量、复合类型与自定义类型、第三方并发结构

以下是 Rust 中标量类型、对象类型&#xff08;含结构体、复合类型、堆分配类型&#xff09;以及常用第三方并发数据结构的完整分类、示例和区别对比&#xff0c;帮助你系统掌握它们的本质异同&#xff1a; &#x1f7e2; 一、标量类型&#xff08;Scalar Types&#xff0c;存储…

基于STM32温湿度检测—串口显示

基于STM32温湿度检测 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.使用DHT11检测温湿度&#xff1b; 2.单片机处理完控制LCD1602显示温湿度&#xff1b; 3.单片机也通过串口显示检测到的温湿度&#xff1b; 添加图片注释&#xff0c;不…

Windows 10 查询 Nginx 进程教程

1. 打开命令提示符&#xff08;CMD&#xff09; 按 Win R&#xff0c;输入 cmd&#xff0c;回车。或者在开始菜单搜索栏输入“cmd”&#xff0c;选择“命令提示符”。 2. 查看是否有正在运行的 Nginx 进程 输入命令&#xff1a; tasklist | findstr nginx这个命令会列出所有…

使用 Kafka 优化物流系统的实践与思考

使用 Kafka 优化物流系统的实践与思考 在现代物流系统中&#xff0c;订单处理、仓储管理、运输调度等环节复杂且实时性要求高。为了满足异步解耦、高吞吐、高可用、事件驱动和数据可靠性等需求&#xff0c;Kafka 作为分布式消息队列和流处理平台&#xff0c;成为了我们的首选。…

Rust中模式匹配let Some(gas_price) = tx.gas_price用法

你问得非常好&#xff0c;let Some(gas_price) tx.gas_price 是 Rust 中的一种模式匹配写法&#xff0c;它用于从 Option 类型中提取值。 ✅ 背景知识&#xff1a;什么是 Option&#xff1f; 在 Rust 中&#xff0c;如果一个值可能存在也可能不存在&#xff0c;就会用 Option…

什么是LLM大语言模型

什么是LLM大语言模型 LLM的全称是&#xff0c;Large Language Model&#xff0c;简称LLM&#xff0c;翻译为大语言模型&#xff0c;其核心是模拟人类语言的复杂规律&#xff0c;实现语义理解、推理分析、文本生成等任务&#xff0c;主要目的是实现能读懂和说出人类语言的模型。…

杂谈-架构时代演进

关于未来 5-10 年软件系统演化方向 1. 云原生 ➝ 超云原生&#xff08;Post Cloud Native&#xff09; Kubernetes 平台自治化&#xff1a; K8s Operator 日益强大&#xff0c;逐步具备自愈、自动扩缩容、自动调优能力。 云厂商与企业私有云逐步融合为一体…

如何查看服务器的运行日志?

&#x1f7e2; 一、Linux服务器 Linux日志都在**/var/log**目录下&#xff0c;最常用的有&#xff1a; &#x1f4c2; 常用日志文件 文件内容/var/log/messages大部分系统日志&#xff08;CentOS常见&#xff09;/var/log/syslog系统消息日志&#xff08;Ubuntu/Debian常见&a…

在幸狐RV1106开发板上用gcc14.2本地编译安装postgresql 17.5数据库

在幸狐RV1106开发板上用gcc14.2本地编译安装postgresql 17.5数据库 编译环境&#xff1a; RV1106G3 Linux luckfox-rv1106 5.10.160 #3 Fri Jun 27 14:16:20 AWST 2025 armv7l GNU/Linux BusyBox v1.36.1 gcc version 14.2.0 (GCC) GNU ld (GNU Binutils) 2.44 GNU Make 4.4 n…

Go语言中map[string]interface{} 和 map[string]string的区别

在 Go 语言中&#xff0c;map[string]interface{} 和 map[string]string 是两种不同类型的 map&#xff0c;它们的主要区别在于值的类型以及这种差异带来的使用场景和灵活性的不同。 1. 值的类型 map[string]interface{}&#xff1a;这里的 interface{} 表示 Go 中的空接口类型…

AdGuard Home 安装及使用

AdGuard Home 是 AdGuard 开源的一个私人 DNS 服务端,只需在网关部署,即可实现全局域网的广告拦截与隐私反追踪。在 DNS 解析的过程中,匹配规则库内的 URL 进行拦截,同时在客户端中,还可以通过自定义过滤规则实现网页 DOM 的拦截。 基于 Golang 编写的 AdGuard Home,官方…

绕过 GraphQL 暴力破解保护

题目要求&#xff1a; 本实验的用户登录机制由 GraphQL API 提供支持。API 终端节点有一个速率限制器&#xff0c;如果它在短时间内收到来自同一源的太多请求&#xff0c;则会返回错误。 要解决实验问题&#xff0c;请暴力破解登录机制以 .使用身份验证实验室密码列表作为密码…

C/C++ 使用rapidjson库 操作Json格式文件(创建、插入、解析、修改、删除)

目录 一、前言 1.简介 2.兼容性 3.下载 4.安装 5.官方文档 6.自述 二、封装Json 1. 创建一个 Document 对象 2. "key":"value" 3. { } 4. [ ] 5. [ { }, { } ] 6. [ [ ], [ ] ] 7. { [ ], [ ] } 8. { { }, { } } 9. 将Document转换为字符串…

免安装一键修复网络诊断 + 权限修复!打印机共享错误工具适配 Win7/10/11

各位打印小能手们&#xff01;你们有没有遇到过共享打印机出问题&#xff0c;搞得自己焦头烂额的情况&#xff1f;比如系统一更新&#xff0c;打印机就连不上&#xff0c;打印任务失败&#xff0c;真的是让人崩溃啊&#xff01;别慌&#xff0c;今天就给大家全面介绍一款打印机…

电脑分屏快捷键5

按window右箭头&#xff1a; 按window左箭头&#xff1a;

nt!CcFlushCache函数分析之nt!CcFindBcb

nt!CcFindBcb函数分析 第一部分&#xff1a; 1: kd> p nt!CcAcquireByteRangeForWrite0x377: 80a13c49 e866e4ffff call nt!CcFindBcb (80a120b4) 1: kd> t nt!CcFindBcb: 80a120b4 55 push ebp 1: kd> kc # 00 nt!CcFindBcb 01 nt!CcAcqu…

矩阵及矩阵快速幂

一.矩阵与模板 【模板】矩阵求和 时间限制&#xff1a;1秒 内存限制&#xff1a;128M 题目描述 给出两个&#x1d45b;行&#x1d45a;列的矩阵&#xff0c;求两个矩阵的和 输入描述 第一行输入两个以空格分隔的整数&#x1d45b;,&#x1d45a;&#xff0c;表示矩…