C++ 命令模式详解

news2025/5/15 2:30:20

命令模式(Command Pattern)是一种行为设计模式,它将请求封装为对象,从而使你可以参数化客户端使用不同的请求、队列或日志请求,以及支持可撤销的操作。

核心概念

设计原则

命令模式遵循以下设计原则:

  1. 单一职责原则:将调用操作的对象与执行操作的对象分离

  2. 开闭原则:可以引入新命令而不修改现有代码

  3. 松耦合:解耦请求发送者和接收者

主要优点

  1. 解耦:分离请求的发送者和接收者

  2. 可扩展:容易添加新命令

  3. 可组合:可以组合多个命令

  4. 支持撤销:可以实现命令的撤销和重做

  5. 延迟执行:支持命令的队列和延迟执行

模式结构

主要组件

  1. Command(命令接口)

    • 声明执行操作的接口

  2. ConcreteCommand(具体命令)

    • 实现命令接口

    • 定义接收者与动作之间的绑定关系

    • 调用接收者的操作

  3. Invoker(调用者)

    • 要求命令执行请求

  4. Receiver(接收者)

    • 知道如何执行与请求相关的操作

  5. 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 {
        // 反向执行undo
        for (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;
        }
    }
};

实际应用场景

  1. GUI操作:菜单项、按钮点击等操作的封装

  2. 事务系统:支持原子操作和回滚

  3. 游戏开发:游戏命令、回放系统

  4. 智能家居:设备控制命令

  5. 任务队列:异步任务执行和调度

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

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

相关文章

制作一款打飞机游戏47:跳转

编辑器的问题 我们开始为不同的敌人编写一些行为&#xff0c;到目前为止进展顺利&#xff0c;一切都很棒。但上次我们遇到了一些问题&#xff0c;我们发现在这个编辑器中编写代码有时有点困难&#xff0c;因为当你想要在某行之间插入内容时&#xff0c;你不得不删除一切然后重…

本地部署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…

vue H5解决安卓手机软键盘弹出,页面高度被顶起

开发中安卓机上遇到的软键盘弹出导致布局问题 直接上代码_ 在这里插入代码片 <div class"container"><div class"appContainer" :style"{height:isKeyboardOpen? Heights :inherit}"><p class"name"><!-- 绑定…

CSS专题之自定义属性

前言 石匠敲击石头的第 12 次 CSS 自定义属性是现代 CSS 的一个强大特性&#xff0c;可以说是前端开发需知、必会的知识点&#xff0c;本篇文章就来好好梳理一下&#xff0c;如果哪里写的有问题欢迎指出。 什么是 CSS 自定义属性 CSS 自定义属性英文全称是 CSS Custom Proper…

七、深入 Hive DDL:管理表、分区与洞察元数据

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月13日 专栏&#xff1a;Hive教程 内容导航 一、表的 DDL 操作 (非创建)二、分区的 DDL 操作三、洞察元数据&#xff1a;SHOW 命令的威力结语&#xff1a;DDL 与 SHOW&#xff0c;Hive 管理的双翼练习题一、选择题二、代码题…

直接在Excel中用Python Matplotlib/Seaborn/Plotly......

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

React面试常问问题详解

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

【Java】网络编程(Socket)

网络编程 Socket 我们开发的网络应用程序位于应用层&#xff0c;TCP和UDP属于传输层协议&#xff0c;在应用层如何使用传输层的服务呢&#xff1f;在应用层和传输层之间&#xff0c;则使用套接字Socket来进行分离 套接字就像是传输层为应用层开的一个小口&#xff0c;应用程…

思科(Cisco ASA/Firepower)、华三(H3C)、华为(Huawei USG)防火墙 的基础配置

以下是针对 思科&#xff08;Cisco ASA/Firepower&#xff09;、华三&#xff08;H3C&#xff09;、华为&#xff08;Huawei USG&#xff09;防火墙 的基础配置指南&#xff0c;涵盖 区域划分、安全策略、NAT、路由 等核心功能。配置示例基于通用场景&#xff0c;实际部署时需根…

Windows环境下maven的安装与配置

1.检查JAVA_HOME环境变量 Maven是使用java开发的&#xff0c;所以必须知道当前系统环境中的JDK的安装目录。 搜索栏直接输入“cmd” 或者 WinR 输入cmd 在打开的终端窗口输入“echo %JAVA_HOME”&#xff0c;就可以看到jdk的位置了。 如果没有的话&#xff0c;请参考我的文章&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 实现表格数据分组功能&#xff0c;不是使用树结构&#xff0c;直接数据分组 查看官网&#xff1a;https://vxetable.cn gitbub&#xff1a;https://github.com/x-extends/vxe-table gitee&#xff1a;https://gitee.com/x-extends/vxe-table 代码 通过…

如何禁止chrome自动更新

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

阳光学院【2020下】计算机网络原理-A卷-试卷-期末考试试卷

一、单选题&#xff08;共25分&#xff0c;每空1分&#xff09; 1.ICMP协议工作在TCP/IP参考模型的 ( ) A.主机-网络 B.网络互联层 C.传输层 D.应用层 2.下列关于交换技术的说法中&#xff0c;错误的是 ( ) A.电路交换适用于突发式通信 B.报文交换不能满足实时通信 C.报文…

kotlin-协程(什么是一个协程)

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

数组和切片的区别

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

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…

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…

解锁 DevOps 新境界 :使用 Flux 进行 GitOps 现场演示 – 自动化您的 Kubernetes 部署

前言 GitOps 是实现持续部署的云原生方式。它的名字来源于标准且占主导地位的版本控制系统 Git。GitOps 的 Git 在某种程度上类似于 Kubernetes 的 etcd&#xff0c;但更进一步&#xff0c;因为 etcd 本身不保存版本历史记录。毋庸置疑&#xff0c;任何源代码管理服务&#xf…

LLM大模型中的基础数学工具—— 信号处理与傅里叶分析

Q51: 推导傅里叶变换 的 Parseval 定理 傅里叶变换的 Parseval 定理揭示了啥关系&#xff1f; Parseval 定理揭示了傅里叶变换中时域与频域的能量守恒关系&#xff0c;即信号在时域的总能量等于其在频域的总能量。这就好比一个物体无论从哪个角度称重&#xff0c;重量始终不…