命令模式实战:从遥控器到分布式任务,解锁灵活调用的终极姿势

📕目录

前言

一、先搞懂:命令模式到底是什么?

1.1 从生活场景理解核心思想

1.2 官方定义与核心角色

1.3 命令模式的核心价值:不止于解耦

二、为什么要用命令模式?优势与适用场景

2.1 核心优势:解决传统开发的 4 大痛点

2.2 适用场景:这些情况用它准没错

2.3 注意:这些场景别用命令模式

三、C++ 实战:从 0 实现命令模式(两个核心案例)

案例 1:基础入门 —— 智能遥控器(支持撤销 / 重做)

需求分析

步骤 1:定义接收者(家电:灯光、电视)

步骤 2:定义抽象命令(Command)

步骤 3:实现具体命令(ConcreteCommand)

步骤 4:定义调用者(Invoker)—— 智能遥控器

步骤 5:客户端测试

运行结果与核心解析

案例 2:高级应用 —— 分布式任务调度系统(支持批量执行 + 日志审计)

需求分析

步骤 1:定义接收者(任务执行器)

步骤 2:定义抽象命令(支持优先级和日志)

步骤 3:实现具体命令(任务命令)

步骤 4:定义调用者(任务调度器)

步骤 5:客户端测试

运行结果与核心解析

四、命令模式的工程化优化:从 “能用” 到 “好用”

4.1 命令的序列化与持久化

4.2 命令的超时控制与重试机制

4.3 命令的监控与告警

4.4 依赖注入(DI)管理接收者

五、避坑指南:实际开发中容易踩的 5 个坑

5.1 过度设计,命令类膨胀

5.2 撤销逻辑复杂,状态不一致

5.3 命令与接收者耦合过紧

5.4 忽略命令的幂等性

5.5 调用者与命令耦合过紧


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

前言

在日常开发中,你是否遇到过这些棘手场景?

  • 想给软件加 “撤销 / 重做” 功能(比如编辑器误删内容、遥控器按错键),直接硬编码逻辑越写越乱;
  • 系统需要支持批量操作(比如一键备份所有数据、批量推送消息),逐个调用接口导致代码臃肿;
  • 模块间耦合严重(比如按钮直接调用灯光开关方法),后续修改灯光逻辑还要改按钮代码;
  • 分布式系统中需要异步执行任务、记录操作日志(比如订单提交后异步扣库存、记日志),同步调用难以满足需求。

这些问题的核心痛点,本质是 “请求的发起与执行强耦合”—— 发起者必须知道接收者的具体实现,才能调用其方法。而命令模式正是解决这类问题的 “万能钥匙”:它将请求封装成独立的 “命令对象”,就像快递包裹一样,包含执行所需的所有信息,让发起者和接收者彻底解耦,还能轻松实现撤销、批量执行、日志记录等高级功能。

今天这篇文章,我们从生活场景切入,用通俗的语言拆解命令模式的核心逻辑,再通过两个完整的 C++ 实战案例(基础入门 + 高级应用),带你从零掌握这个设计模式,最后分享工程化优化技巧和避坑指南,确保你学完就能用在实际项目中,文章全程无 AI 腔、全是干货,建议收藏慢慢看~

一、先搞懂:命令模式到底是什么?

1.1 从生活场景理解核心思想

命令模式的逻辑在生活中随处可见,最典型的就是快递收发流程

  • 你(寄件人,请求发起者)把要寄的东西打包好,填写快递单(命令对象),上面写着收件人地址、联系方式、货物信息(执行请求的所有参数);
  • 你把包裹交给快递员(调用者),不需要知道后续怎么运输、谁来派送,只需告诉快递员 “帮忙寄出去”(触发命令);
  • 快递员把包裹交给快递公司(接收者),快递公司通过分拣、运输,最终把包裹送到收件人手中(执行命令)。

再比如餐厅点餐

  • 顾客(发起者)点单后,服务员(调用者)把菜单(命令对象)交给后厨(接收者);
  • 顾客不用知道后厨怎么做菜、用什么食材,只需等待上菜;后厨不用知道谁点的单,只需按菜单做菜。

这里的核心逻辑是:请求发起者不需要直接调用接收者的方法,而是通过 “命令对象” 作为中间载体传递请求。命令对象就像 “万能接口”,隔绝了发起者和接收者的直接依赖,让两者各司其职、互不干扰。

1.2 官方定义与核心角色

用技术语言来说,命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

命令模式包含五个核心角色,用 “快递流程” 对应解释,一看就懂:

角色官方定义快递流程对应通俗理解
抽象命令(Command)定义命令的统一接口,包含执行命令的抽象方法(如execute())和撤销方法(如undo())。快递单模板(统一的格式)规定 “命令必须有执行 / 撤销功能”
具体命令(ConcreteCommand)实现抽象命令接口,封装具体请求:持有接收者引用,在execute()中调用接收者的具体方法。你的快递单(填写了具体信息)具体的 “做事指令”,知道 “找谁做、做什么”
调用者(Invoker)持有命令对象,负责触发命令执行(调用execute()),不关心命令的具体实现。快递员“传话筒”,负责把命令交给接收者执行
接收者(Receiver)实际执行命令的对象,包含命令所需的具体业务逻辑(如 “送货”“做菜”)。快递公司、后厨真正 “做事” 的角色,有具体的业务能力
客户端(Client)创建具体命令对象,将命令与接收者关联,组装调用者和命令(如填写快递单、绑定按钮与命令)。寄件人、顾客“组织者”,负责把命令、调用者、接收者串联起来

1.3 命令模式的核心价值:不止于解耦

命令模式的本质是 “请求的对象化”,它带来的核心价值远不止解耦:

  1. 解耦发起者与接收者:发起者无需知道接收者的存在,也不用知道接收者如何执行请求(比如你按遥控器时,不用懂灯光的电路原理);
  2. 命令可复用、可传递:命令对象是独立的,可以像普通对象一样传递、存储(比如遥控器的 “历史命令” 列表,用于撤销);
  3. 支持高级功能:基于命令对象,能轻松实现撤销 / 重做、批量执行、日志记录、事务管理等(比如编辑器的撤销、数据库的事务回滚);
  4. 扩展灵活:新增命令时,只需创建新的具体命令类,无需修改发起者、接收者的代码(符合开闭原则),比如给遥控器加 “空调调温” 命令,不用改遥控器本身。

二、为什么要用命令模式?优势与适用场景

2.1 核心优势:解决传统开发的 4 大痛点

相比于 “发起者直接调用接收者方法” 的传统方式,命令模式的优势非常明显:

传统开发痛点命令模式解决方案
发起者与接收者强耦合发起者只调用命令的execute(),不依赖接收者,耦合度降至最低
无法支持撤销 / 重做命令对象记录执行前的状态,undo()方法恢复状态(比如灯光开之前是关,撤销时关灯)
批量操作代码臃肿将多个命令组合成 “复合命令”,一键执行 / 回滚,类似事务的 “原子性”
新增功能需修改原有代码新增命令只需创建新的ConcreteCommand类,无需修改发起者、接收者(符合开闭原则)

举个具体例子:传统方式实现遥控器开灯,代码是这样的:

// 传统方式:发起者(遥控器)直接依赖接收者(灯光)
class RemoteControl {
public:
    void turnOnLight(Light& light) {
        light.turnOn();  // 强耦合:遥控器必须知道Light类的turnOn()方法
    }
};

如果后续灯光类改了方法名(比如turnOn()改为switchOn()),或者要换空调,遥控器代码必须跟着改。而命令模式中,遥控器只依赖抽象命令,不关心具体是灯光还是空调:

// 命令模式:发起者(遥控器)依赖抽象命令,不依赖接收者
class RemoteControl {
public:
    void setCommand(Command* cmd) { m_cmd = cmd; }
    void pressButton() { m_cmd->execute(); }  // 只调用命令的统一接口
private:
    Command* m_cmd;
};

无论接收者是灯光、空调还是电视,只要对应的命令实现了execute(),遥控器都能正常工作,无需修改一行代码。

2.2 适用场景:这些情况用它准没错

命令模式不是 “万能的”,但在以下场景中,它能发挥最大价值:

  • 需要解耦发起者与接收者:比如框架设计(GUI 框架的按钮与功能模块)、插件系统(插件与主程序);
  • 需要支持撤销 / 重做:比如编辑器、绘图软件、遥控器、游戏存档;
  • 需要批量执行或事务管理:比如数据库事务(提交 / 回滚)、批量任务调度(一键备份、批量导出);
  • 需要记录操作日志:比如操作审计(记录谁做了什么)、故障恢复(通过日志重新执行命令);
  • 需要动态指定和执行请求:比如分布式系统的任务队列、消息队列(异步执行命令)。

2.3 注意:这些场景别用命令模式

命令模式也有局限性,以下情况不建议使用,否则会过度设计:

  • 简单的单次请求调用:比如只是调用一个简单的工具函数(如calculate(1+2)),无需解耦和扩展;
  • 命令数量极少且不会扩展:比如只有 1-2 个固定命令,直接调用比封装命令更简洁;
  • 不需要撤销、批量执行等高级功能:比如简单的 CRUD 操作,用命令模式会增加代码复杂度。

三、C++ 实战:从 0 实现命令模式(两个核心案例)

下面通过两个实战案例,带你掌握命令模式的 C++ 实现。所有代码均为原创,无版权问题,使用 C++11 及以上标准,加入智能指针、日志记录等工程化细节,可直接复制运行。

案例 1:基础入门 —— 智能遥控器(支持撤销 / 重做)

需求分析

实现一个智能遥控器,支持以下功能:

  • 控制两种家电:灯光(开 / 关、调亮度)、电视(开 / 关、调音量);
  • 遥控器有 4 个功能按钮(绑定具体命令)和 2 个特殊按钮(撤销、重做);
  • 支持连续撤销 / 重做(比如连续按 3 次开灯,撤销 3 次回到初始状态);
  • 避免内存泄漏,使用智能指针管理对象。
步骤 1:定义接收者(家电:灯光、电视)

接收者是实际执行命令的对象,包含具体的业务逻辑(比如开灯、调音量)。

#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <memory>  // 智能指针
#include <ctime>   // 日志时间

// 工具函数:获取当前时间(用于日志)
std::string getCurrentTime() {
    time_t now = time(nullptr);
    tm* t = localtime(&now);
    char buf[64];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", t);
    return buf;
}

// 接收者1:灯光
class Light {
public:
    Light(std::string name) : m_name(name), m_isOn(false), m_brightness(100) {}

    // 开灯
    void turnOn() {
        if (!m_isOn) {
            m_isOn = true;
            std::cout << "[" << getCurrentTime() << "] [灯光] " << m_name << " 已打开,亮度:" << m_brightness << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [灯光] " << m_name << " 已经是打开状态" << std::endl;
        }
    }

    // 关灯
    void turnOff() {
        if (m_isOn) {
            m_isOn = false;
            std::cout << "[" << getCurrentTime() << "] [灯光] " << m_name << " 已关闭" << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [灯光] " << m_name << " 已经是关闭状态" << std::endl;
        }
    }

    // 调亮度(0-100)
    void adjustBrightness(int value) {
        if (value < 0) value = 0;
        if (value > 100) value = 100;
        m_brightness = value;
        if (m_isOn) {
            std::cout << "[" << getCurrentTime() << "] [灯光] " << m_name << " 亮度调整为:" << m_brightness << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [灯光] " << m_name << " 已关闭,调整亮度无效" << std::endl;
        }
    }

    // 获取当前状态(用于撤销)
    bool isOn() const { return m_isOn; }
    int getBrightness() const { return m_brightness; }

private:
    std::string m_name;       // 设备名称(如“客厅灯光”)
    bool m_isOn;              // 开关状态
    int m_brightness;         // 亮度(0-100)
};

// 接收者2:电视
class TV {
public:
    TV(std::string name) : m_name(name), m_isOn(false), m_volume(10) {}

    // 开电视
    void turnOn() {
        if (!m_isOn) {
            m_isOn = true;
            std::cout << "[" << getCurrentTime() << "] [电视] " << m_name << " 已打开,音量:" << m_volume << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [电视] " << m_name << " 已经是打开状态" << std::endl;
        }
    }

    // 关电视
    void turnOff() {
        if (m_isOn) {
            m_isOn = false;
            std::cout << "[" << getCurrentTime() << "] [电视] " << m_name << " 已关闭" << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [电视] " << m_name << " 已经是关闭状态" << std::endl;
        }
    }

    // 调音量(0-30)
    void adjustVolume(int value) {
        if (value < 0) value = 0;
        if (value > 30) value = 30;
        m_volume = value;
        if (m_isOn) {
            std::cout << "[" << getCurrentTime() << "] [电视] " << m_name << " 音量调整为:" << m_volume << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [电视] " << m_name << " 已关闭,调整音量无效" << std::endl;
        }
    }

    // 获取当前状态(用于撤销)
    bool isOn() const { return m_isOn; }
    int getVolume() const { return m_volume; }

private:
    std::string m_name;       // 设备名称(如“客厅电视”)
    bool m_isOn;              // 开关状态
    int m_volume;             // 音量(0-30)
};
步骤 2:定义抽象命令(Command)

抽象命令定义统一接口,包含execute()(执行命令)和undo()(撤销命令)两个核心方法。

// 抽象命令类
class Command {
public:
    virtual ~Command() = default;  // 虚析构,确保子类正确析构
    virtual void execute() = 0;    // 执行命令(纯虚方法,子类必须实现)
    virtual void undo() = 0;       // 撤销命令(纯虚方法,子类必须实现)
};
步骤 3:实现具体命令(ConcreteCommand)

每个具体命令对应一个接收者的操作,比如 “灯光开命令”“电视调音量命令”。命令对象持有接收者的引用,并记录执行前的状态,用于撤销。

// 具体命令1:灯光开命令
class LightOnCommand : public Command {
public:
    // 构造函数:关联接收者(灯光)
    LightOnCommand(std::shared_ptr<Light> light) : m_light(light) {}

    // 执行命令:调用灯光的开灯方法
    void execute() override {
        // 记录执行前的状态(用于撤销)
        m_prevState = m_light->isOn();
        m_light->turnOn();
    }

    // 撤销命令:恢复执行前的状态
    void undo() override {
        std::cout << "[" << getCurrentTime() << "] [撤销] ";
        if (m_prevState) {
            std::cout << "灯光之前已是打开状态,无需撤销" << std::endl;
        } else {
            m_light->turnOff();  // 执行前是关闭,撤销时关闭
        }
    }

private:
    std::shared_ptr<Light> m_light;  // 持有接收者(智能指针,自动管理内存)
    bool m_prevState;                // 执行前的开关状态
};

// 具体命令2:灯光关命令
class LightOffCommand : public Command {
public:
    LightOffCommand(std::shared_ptr<Light> light) : m_light(light) {}

    void execute() override {
        m_prevState = m_light->isOn();
        m_light->turnOff();
    }

    void undo() override {
        std::cout << "[" << getCurrentTime() << "] [撤销] ";
        if (m_prevState) {
            m_light->turnOn();  // 执行前是打开,撤销时打开
        } else {
            std::cout << "灯光之前已是关闭状态,无需撤销" << std::endl;
        }
    }

private:
    std::shared_ptr<Light> m_light;
    bool m_prevState;
};

// 具体命令3:灯光调亮度命令
class LightAdjustBrightnessCommand : public Command {
public:
    LightAdjustBrightnessCommand(std::shared_ptr<Light> light, int targetBrightness)
        : m_light(light), m_targetBrightness(targetBrightness) {}

    void execute() override {
        // 记录执行前的亮度(用于撤销)
        m_prevBrightness = m_light->getBrightness();
        m_light->adjustBrightness(m_targetBrightness);
    }

    void undo() override {
        std::cout << "[" << getCurrentTime() << "] [撤销] ";
        m_light->adjustBrightness(m_prevBrightness);  // 恢复之前的亮度
    }

private:
    std::shared_ptr<Light> m_light;
    int m_targetBrightness;  // 目标亮度
    int m_prevBrightness;    // 执行前的亮度
};

// 具体命令4:电视开命令
class TVOnCommand : public Command {
public:
    TVOnCommand(std::shared_ptr<TV> tv) : m_tv(tv) {}

    void execute() override {
        m_prevState = m_tv->isOn();
        m_tv->turnOn();
    }

    void undo() override {
        std::cout << "[" << getCurrentTime() << "] [撤销] ";
        if (m_prevState) {
            std::cout << "电视之前已是打开状态,无需撤销" << std::endl;
        } else {
            m_tv->turnOff();
        }
    }

private:
    std::shared_ptr<TV> m_tv;
    bool m_prevState;
};

// 具体命令5:电视调音量命令
class TVAdjustVolumeCommand : public Command {
public:
    TVAdjustVolumeCommand(std::shared_ptr<TV> tv, int targetVolume)
        : m_tv(tv), m_targetVolume(targetVolume) {}

    void execute() override {
        m_prevVolume = m_tv->getVolume();
        m_tv->adjustVolume(m_targetVolume);
    }

    void undo() override {
        std::cout << "[" << getCurrentTime() << "] [撤销] ";
        m_tv->adjustVolume(m_prevVolume);
    }

private:
    std::shared_ptr<TV> m_tv;
    int m_targetVolume;  // 目标音量
    int m_prevVolume;    // 执行前的音量
};
步骤 4:定义调用者(Invoker)—— 智能遥控器

调用者持有命令对象,负责触发命令执行、撤销、重做。遥控器有 4 个功能按钮(绑定具体命令),以及撤销 / 重做按钮,用栈记录执行历史和撤销历史。

class SmartRemoteControl {
public:
    SmartRemoteControl() {}

    // 给按钮绑定命令(buttonIndex:0-3,对应4个功能按钮)
    void setCommand(int buttonIndex, std::shared_ptr<Command> cmd) {
        if (buttonIndex >= 0 && buttonIndex < 4) {
            m_buttons[buttonIndex] = cmd;
            std::cout << "[" << getCurrentTime() << "] [遥控器] 按钮" << buttonIndex << "绑定命令成功" << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [遥控器] 按钮索引无效(仅支持0-3)" << std::endl;
        }
    }

    // 按下功能按钮(触发命令执行)
    void pressButton(int buttonIndex) {
        if (buttonIndex >= 0 && buttonIndex < 4 && m_buttons[buttonIndex]) {
            std::cout << "\n[" << getCurrentTime() << "] [遥控器] 按下按钮" << buttonIndex << std::endl;
            m_buttons[buttonIndex]->execute();
            // 记录执行历史(用于撤销)
            m_executedCommands.push(m_buttons[buttonIndex]);
            // 撤销后又执行新命令,清空重做历史
            while (!m_undoneCommands.empty()) {
                m_undoneCommands.pop();
            }
        } else {
            std::cout << "\n[" << getCurrentTime() << "] [遥控器] 按钮" << buttonIndex << "未绑定命令" << std::endl;
        }
    }

    // 按下撤销按钮
    void pressUndoButton() {
        std::cout << "\n[" << getCurrentTime() << "] [遥控器] 按下撤销按钮" << std::endl;
        if (!m_executedCommands.empty()) {
            auto cmd = m_executedCommands.top();
            cmd->undo();
            // 移到重做栈
            m_undoneCommands.push(cmd);
            m_executedCommands.pop();
        } else {
            std::cout << "[" << getCurrentTime() << "] [遥控器] 没有可撤销的操作" << std::endl;
        }
    }

    // 按下重做按钮
    void pressRedoButton() {
        std::cout << "\n[" << getCurrentTime() << "] [遥控器] 按下重做按钮" << std::endl;
        if (!m_undoneCommands.empty()) {
            auto cmd = m_undoneCommands.top();
            std::cout << "[" << getCurrentTime() << "] [遥控器] 重做操作" << std::endl;
            cmd->execute();
            // 移回执行栈
            m_executedCommands.push(cmd);
            m_undoneCommands.pop();
        } else {
            std::cout << "[" << getCurrentTime() << "] [遥控器] 没有可重做的操作" << std::endl;
        }
    }

private:
    // 4个功能按钮,存储绑定的命令
    std::array<std::shared_ptr<Command>, 4> m_buttons;
    // 执行历史栈(用于撤销)
    std::stack<std::shared_ptr<Command>> m_executedCommands;
    // 撤销历史栈(用于重做)
    std::stack<std::shared_ptr<Command>> m_undoneCommands;
};
步骤 5:客户端测试

客户端创建接收者、具体命令、调用者,绑定命令到遥控器,测试功能按钮、撤销、重做。

int main() {
    // 1. 创建接收者(家电)
    auto livingRoomLight = std::make_shared<Light>("客厅灯光");
    auto livingRoomTV = std::make_shared<TV>("客厅电视");

    // 2. 创建具体命令
    auto lightOnCmd = std::make_shared<LightOnCommand>(livingRoomLight);
    auto lightAdjustCmd = std::make_shared<LightAdjustBrightnessCommand>(livingRoomLight, 50);
    auto tvOnCmd = std::make_shared<TVOnCommand>(livingRoomTV);
    auto tvAdjustVolumeCmd = std::make_shared<TVAdjustVolumeCommand>(livingRoomTV, 20);

    // 3. 创建调用者(智能遥控器)并绑定命令
    SmartRemoteControl remote;
    remote.setCommand(0, lightOnCmd);         // 按钮0:灯光开
    remote.setCommand(1, lightAdjustCmd);     // 按钮1:灯光调至50亮度
    remote.setCommand(2, tvOnCmd);            // 按钮2:电视开
    remote.setCommand(3, tvAdjustVolumeCmd);  // 按钮3:电视调至20音量

    // 4. 测试功能按钮
    remote.pressButton(0);  // 打开灯光
    remote.pressButton(1);  // 灯光调至50亮度
    remote.pressButton(2);  // 打开电视
    remote.pressButton(3);  // 电视调至20音量

    // 5. 测试撤销
    remote.pressUndoButton();  // 撤销“电视调音量”→ 音量恢复10
    remote.pressUndoButton();  // 撤销“电视开”→ 电视关闭
    remote.pressUndoButton();  // 撤销“灯光调亮度”→ 亮度恢复100
    remote.pressUndoButton();  // 撤销“灯光开”→ 灯光关闭
    remote.pressUndoButton();  // 无可撤销操作

    // 6. 测试重做
    remote.pressRedoButton();  // 重做“灯光开”→ 灯光打开
    remote.pressRedoButton();  // 重做“灯光调亮度”→ 亮度50
    remote.pressRedoButton();  // 重做“电视开”→ 电视打开
    remote.pressRedoButton();  // 重做“电视调音量”→ 音量20
    remote.pressRedoButton();  // 无可重做操作

    return 0;
}
运行结果与核心解析
[2024-06-01 14:30:00] [遥控器] 按钮0绑定命令成功
[2024-06-01 14:30:00] [遥控器] 按钮1绑定命令成功
[2024-06-01 14:30:00] [遥控器] 按钮2绑定命令成功
[2024-06-01 14:30:00] [遥控器] 按钮3绑定命令成功

[2024-06-01 14:30:00] [遥控器] 按下按钮0
[2024-06-01 14:30:00] [灯光] 客厅灯光 已打开,亮度:100

[2024-06-01 14:30:00] [遥控器] 按下按钮1
[2024-06-01 14:30:00] [灯光] 客厅灯光 亮度调整为:50

[2024-06-01 14:30:00] [遥控器] 按下按钮2
[2024-06-01 14:30:00] [电视] 客厅电视 已打开,音量:10

[2024-06-01 14:30:00] [遥控器] 按下按钮3
[2024-06-01 14:30:00] [电视] 客厅电视 音量调整为:20

[2024-06-01 14:30:00] [遥控器] 按下撤销按钮
[2024-06-01 14:30:00] [撤销] [2024-06-01 14:30:00] [电视] 客厅电视 音量调整为:10

[2024-06-01 14:30:00] [遥控器] 按下撤销按钮
[2024-06-01 14:30:00] [撤销] [2024-06-01 14:30:00] [电视] 客厅电视 已关闭

[2024-06-01 14:30:00] [遥控器] 按下撤销按钮
[2024-06-01 14:30:00] [撤销] [2024-06-01 14:30:00] [灯光] 客厅灯光 亮度调整为:100

[2024-06-01 14:30:00] [遥控器] 按下撤销按钮
[2024-06-01 14:30:00] [撤销] [2024-06-01 14:30:00] [灯光] 客厅灯光 已关闭

[2024-06-01 14:30:00] [遥控器] 按下撤销按钮
[2024-06-01 14:30:00] [遥控器] 没有可撤销的操作

[2024-06-01 14:30:00] [遥控器] 按下重做按钮
[2024-06-01 14:30:00] [遥控器] 重做操作
[2024-06-01 14:30:00] [灯光] 客厅灯光 已打开,亮度:100

[2024-06-01 14:30:00] [遥控器] 按下重做按钮
[2024-06-01 14:30:00] [遥控器] 重做操作
[2024-06-01 14:30:00] [灯光] 客厅灯光 亮度调整为:50

[2024-06-01 14:30:00] [遥控器] 按下重做按钮
[2024-06-01 14:30:00] [遥控器] 重做操作
[2024-06-01 14:30:00] [电视] 客厅电视 已打开,音量:10

[2024-06-01 14:30:00] [遥控器] 按下重做按钮
[2024-06-01 14:30:00] [遥控器] 重做操作
[2024-06-01 14:30:00] [电视] 客厅电视 音量调整为:20

[2024-06-01 14:30:00] [遥控器] 按下重做按钮
[2024-06-01 14:30:00] [遥控器] 没有可重做的操作

核心亮点

  • 解耦彻底:遥控器(调用者)不知道灯光、电视(接收者)的具体实现,只依赖抽象命令;
  • 撤销 / 重做:用两个栈分别记录执行历史和撤销历史,支持连续撤销 / 重做;
  • 内存安全:全程使用std::shared_ptr管理对象,无需手动释放,避免内存泄漏;
  • 扩展性强:新增 “空调调温” 命令,只需创建AirConditioner接收者和对应的命令类,无需修改遥控器代码。

案例 2:高级应用 —— 分布式任务调度系统(支持批量执行 + 日志审计)

需求分析

实现一个分布式任务调度系统,模拟实际项目中的命令模式应用,支持以下功能:

  • 支持多种任务类型:文件备份、数据同步、日志清理;
  • 支持批量添加任务,一键执行所有任务(复合命令);
  • 支持批量撤销所有已执行任务(事务回滚);
  • 记录每个任务的执行日志(时间、执行人、任务详情),支持导出日志;
  • 支持任务优先级调度(高优先级任务先执行)。
步骤 1:定义接收者(任务执行器)

接收者包含具体的任务逻辑:文件备份、数据同步、日志清理。

#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <memory>
#include <ctime>
#include <mutex>  // 线程安全

// 工具函数:获取当前时间
std::string getCurrentTime() {
    time_t now = time(nullptr);
    tm* t = localtime(&now);
    char buf[64];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", t);
    return buf;
}

// 接收者1:文件备份执行器
class BackupExecutor {
public:
    void backupFile(const std::string& srcPath, const std::string& destPath, const std::string& operatorName) {
        std::lock_guard<std::mutex> lock(m_mutex);  // 线程安全
        m_lastBackupInfo = "源路径:" + srcPath + ",目标路径:" + destPath + ",执行人:" + operatorName;
        std::cout << "[" << getCurrentTime() << "] [文件备份] 开始执行:" << m_lastBackupInfo << std::endl;
        // 模拟备份耗时
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::cout << "[" << getCurrentTime() << "] [文件备份] 执行成功" << std::endl;
    }

    void undoBackup() {
        std::lock_guard<std::mutex> lock(m_mutex);
        std::cout << "[" << getCurrentTime() << "] [文件备份] 撤销操作:删除备份文件(" << m_lastBackupInfo << ")" << std::endl;
        m_lastBackupInfo.clear();
    }

    std::string getLastBackupInfo() const { return m_lastBackupInfo; }

private:
    std::string m_lastBackupInfo;  // 上次备份信息
    std::mutex m_mutex;            // 互斥锁,保证线程安全
};

// 接收者2:数据同步执行器
class SyncExecutor {
public:
    void syncData(const std::string& dbSource, const std::string& dbTarget, const std::string& operatorName) {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_lastSyncInfo = "源数据库:" + dbSource + ",目标数据库:" + dbTarget + ",执行人:" + operatorName;
        std::cout << "[" << getCurrentTime() << "] [数据同步] 开始执行:" << m_lastSyncInfo << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(800));
        std::cout << "[" << getCurrentTime() << "] [数据同步] 执行成功" << std::endl;
    }

    void undoSync() {
        std::lock_guard<std::mutex> lock(m_mutex);
        std::cout << "[" << getCurrentTime() << "] [数据同步] 撤销操作:回滚同步数据(" << m_lastSyncInfo << ")" << std::endl;
        m_lastSyncInfo.clear();
    }

    std::string getLastSyncInfo() const { return m_lastSyncInfo; }

private:
    std::string m_lastSyncInfo;
    std::mutex m_mutex;
};

// 接收者3:日志清理执行器
class LogCleanExecutor {
public:
    void cleanLog(const std::string& logPath, int keepDays, const std::string& operatorName) {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_lastCleanInfo = "日志路径:" + logPath + ",保留天数:" + std::to_string(keepDays) + ",执行人:" + operatorName;
        std::cout << "[" << getCurrentTime() << "] [日志清理] 开始执行:" << m_lastCleanInfo << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
        std::cout << "[" << getCurrentTime() << "] [日志清理] 执行成功" << std::endl;
    }

    void undoClean() {
        std::lock_guard<std::mutex> lock(m_mutex);
        std::cout << "[" << getCurrentTime() << "] [日志清理] 撤销操作:恢复清理的日志(" << m_lastCleanInfo << ")" << std::endl;
        m_lastCleanInfo.clear();
    }

    std::string getLastCleanInfo() const { return m_lastCleanInfo; }

private:
    std::string m_lastCleanInfo;
    std::mutex m_mutex;
};
步骤 2:定义抽象命令(支持优先级和日志)

抽象命令新增优先级属性和日志接口,支持优先级调度和日志审计。

// 抽象命令类(支持优先级和日志)
class TaskCommand : public Command {
public:
    // 任务优先级:高、中、低
    enum class Priority { HIGH, MEDIUM, LOW };

    TaskCommand(Priority priority, const std::string& operatorName)
        : m_priority(priority), m_operatorName(operatorName), m_isExecuted(false) {}

    virtual ~TaskCommand() = default;

    // 获取优先级(用于调度)
    Priority getPriority() const { return m_priority; }

    // 获取执行日志(用于审计)
    virtual std::string getLog() const = 0;

    // 命令是否已执行
    bool isExecuted() const { return m_isExecuted; }

protected:
    Priority m_priority;        // 任务优先级
    std::string m_operatorName; // 执行人
    bool m_isExecuted;          // 执行状态
};
步骤 3:实现具体命令(任务命令)

每个任务命令对应一个接收者的操作,支持执行、撤销、日志记录,以及优先级。

// 具体命令1:文件备份命令
class BackupCommand : public TaskCommand {
public:
    BackupCommand(std::shared_ptr<BackupExecutor> executor, const std::string& srcPath, const std::string& destPath,
                  const std::string& operatorName, Priority priority = Priority::MEDIUM)
        : TaskCommand(priority, operatorName), m_executor(executor), m_srcPath(srcPath), m_destPath(destPath) {}

    void execute() override {
        if (!m_isExecuted) {
            m_executor->backupFile(m_srcPath, m_destPath, m_operatorName);
            m_isExecuted = true;
        } else {
            std::cout << "[" << getCurrentTime() << "] [文件备份命令] 已执行过,无需重复执行" << std::endl;
        }
    }

    void undo() override {
        if (m_isExecuted) {
            m_executor->undoBackup();
            m_isExecuted = false;
        } else {
            std::cout << "[" << getCurrentTime() << "] [文件备份命令] 未执行,无法撤销" << std::endl;
        }
    }

    std::string getLog() const override {
        std::string priorityStr = (m_priority == Priority::HIGH) ? "高" : (m_priority == Priority::MEDIUM) ? "中" : "低";
        return "[" + getCurrentTime() + "] [任务类型:文件备份] [优先级:" + priorityStr + "] [执行人:" + m_operatorName + "] [详情:" + m_executor->getLastBackupInfo() + "] [执行状态:" + (m_isExecuted ? "成功" : "未执行") + "]";
    }

private:
    std::shared_ptr<BackupExecutor> m_executor;
    std::string m_srcPath;      // 源路径
    std::string m_destPath;     // 目标路径
};

// 具体命令2:数据同步命令
class SyncCommand : public TaskCommand {
public:
    SyncCommand(std::shared_ptr<SyncExecutor> executor, const std::string& dbSource, const std::string& dbTarget,
                const std::string& operatorName, Priority priority = Priority::HIGH)
        : TaskCommand(priority, operatorName), m_executor(executor), m_dbSource(dbSource), m_dbTarget(dbTarget) {}

    void execute() override {
        if (!m_isExecuted) {
            m_executor->syncData(m_dbSource, m_dbTarget, m_operatorName);
            m_isExecuted = true;
        } else {
            std::cout << "[" << getCurrentTime() << "] [数据同步命令] 已执行过,无需重复执行" << std::endl;
        }
    }

    void undo() override {
        if (m_isExecuted) {
            m_executor->undoSync();
            m_isExecuted = false;
        } else {
            std::cout << "[" << getCurrentTime() << "] [数据同步命令] 未执行,无法撤销" << std::endl;
        }
    }

    std::string getLog() const override {
        std::string priorityStr = (m_priority == Priority::HIGH) ? "高" : (m_priority == Priority::MEDIUM) ? "中" : "低";
        return "[" + getCurrentTime() + "] [任务类型:数据同步] [优先级:" + priorityStr + "] [执行人:" + m_operatorName + "] [详情:" + m_executor->getLastSyncInfo() + "] [执行状态:" + (m_isExecuted ? "成功" : "未执行") + "]";
    }

private:
    std::shared_ptr<SyncExecutor> m_executor;
    std::string m_dbSource;     // 源数据库
    std::string m_dbTarget;     // 目标数据库
};

// 具体命令3:日志清理命令
class LogCleanCommand : public TaskCommand {
public:
    LogCleanCommand(std::shared_ptr<LogCleanExecutor> executor, const std::string& logPath, int keepDays,
                    const std::string& operatorName, Priority priority = Priority::LOW)
        : TaskCommand(priority, operatorName), m_executor(executor), m_logPath(logPath), m_keepDays(keepDays) {}

    void execute() override {
        if (!m_isExecuted) {
            m_executor->cleanLog(m_logPath, m_keepDays, m_operatorName);
            m_isExecuted = true;
        } else {
            std::cout << "[" << getCurrentTime() << "] [日志清理命令] 已执行过,无需重复执行" << std::endl;
        }
    }

    void undo() override {
        if (m_isExecuted) {
            m_executor->undoClean();
            m_isExecuted = false;
        } else {
            std::cout << "[" << getCurrentTime() << "] [日志清理命令] 未执行,无法撤销" << std::endl;
        }
    }

    std::string getLog() const override {
        std::string priorityStr = (m_priority == Priority::HIGH) ? "高" : (m_priority == Priority::MEDIUM) ? "中" : "低";
        return "[" + getCurrentTime() + "] [任务类型:日志清理] [优先级:" + priorityStr + "] [执行人:" + m_operatorName + "] [详情:" + m_executor->getLastCleanInfo() + "] [执行状态:" + (m_isExecuted ? "成功" : "未执行") + "]";
    }

private:
    std::shared_ptr<LogCleanExecutor> m_executor;
    std::string m_logPath;      // 日志路径
    int m_keepDays;             // 保留天数
};

// 复合命令:批量任务命令(组合多个子命令,支持批量执行/撤销)
class BatchTaskCommand : public TaskCommand {
public:
    BatchTaskCommand(const std::string& operatorName)
        : TaskCommand(Priority::MEDIUM, operatorName) {}

    // 添加子命令(按优先级排序)
    void addSubCommand(std::shared_ptr<TaskCommand> cmd) {
        m_subCommands.push(cmd);
    }

    // 执行所有子命令(按优先级从高到低执行)
    void execute() override {
        if (!m_isExecuted) {
            std::cout << "\n[" << getCurrentTime() << "] [批量任务] 开始执行,共" << m_subCommands.size() << "个子任务(按优先级排序)" << std::endl;
            // 临时栈,用于保存执行后的命令(保持原优先级队列不变)
            std::priority_queue<std::shared_ptr<TaskCommand>, std::vector<std::shared_ptr<TaskCommand>>, ComparePriority> tempQueue = m_subCommands;
            while (!tempQueue.empty()) {
                auto cmd = tempQueue.top();
                cmd->execute();
                if (cmd->isExecuted()) {
                    m_executedSubCommands.push(cmd);  // 记录已执行的子命令(用于撤销)
                }
                tempQueue.pop();
            }
            m_isExecuted = true;
            std::cout << "[" << getCurrentTime() << "] [批量任务] 执行完成" << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [批量任务] 已执行过,无需重复执行" << std::endl;
        }
    }

    // 撤销所有已执行的子命令(逆序撤销,保证依赖关系)
    void undo() override {
        if (m_isExecuted) {
            std::cout << "\n[" << getCurrentTime() << "] [批量任务] 开始撤销,共" << m_executedSubCommands.size() << "个子任务" << std::endl;
            while (!m_executedSubCommands.empty()) {
                auto cmd = m_executedSubCommands.top();
                cmd->undo();
                m_executedSubCommands.pop();
            }
            m_isExecuted = false;
            std::cout << "[" << getCurrentTime() << "] [批量任务] 撤销完成" << std::endl;
        } else {
            std::cout << "[" << getCurrentTime() << "] [批量任务] 未执行,无法撤销" << std::endl;
        }
    }

    // 获取批量任务的完整日志
    std::string getLog() const override {
        std::string log = "[" + getCurrentTime() + "] [批量任务] [执行人:" + m_operatorName + "] [执行状态:" + (m_isExecuted ? "成功" : "未执行") + "] [子任务数量:" + std::to_string(m_subCommands.size()) + "]\n";
        std::priority_queue<std::shared_ptr<TaskCommand>, std::vector<std::shared_ptr<TaskCommand>>, ComparePriority> tempQueue = m_subCommands;
        while (!tempQueue.empty()) {
            log += "  - " + tempQueue.top()->getLog() + "\n";
            tempQueue.pop();
        }
        return log;
    }

private:
    // 优先级比较函数:高优先级在前
    struct ComparePriority {
        bool operator()(const std::shared_ptr<TaskCommand>& a, const std::shared_ptr<TaskCommand>& b) {
            return a->getPriority() < b->getPriority();
        }
    };

    // 子命令优先级队列(按优先级排序)
    std::priority_queue<std::shared_ptr<TaskCommand>, std::vector<std::shared_ptr<TaskCommand>>, ComparePriority> m_subCommands;
    // 已执行的子命令栈(用于撤销)
    std::stack<std::shared_ptr<TaskCommand>> m_executedSubCommands;
};
步骤 4:定义调用者(任务调度器)

调用者负责管理批量任务,支持添加任务、执行、撤销、导出日志。

// 调用者:任务调度器
class TaskScheduler {
public:
    // 创建批量任务
    std::shared_ptr<BatchTaskCommand> createBatchTask(const std::string& operatorName) {
        auto batchCmd = std::make_shared<BatchTaskCommand>(operatorName);
        m_currentBatchTask = batchCmd;
        return batchCmd;
    }

    // 执行当前批量任务
    void executeBatchTask() {
        if (m_currentBatchTask) {
            m_currentBatchTask->execute();
            // 记录批量任务日志
            m_taskLogs.push_back(m_currentBatchTask->getLog());
        } else {
            std::cout << "[" << getCurrentTime() << "] [任务调度器] 未创建批量任务" << std::endl;
        }
    }

    // 撤销当前批量任务
    void undoBatchTask() {
        if (m_currentBatchTask) {
            m_currentBatchTask->undo();
            // 记录撤销日志
            m_taskLogs.push_back(m_currentBatchTask->getLog());
        } else {
            std::cout << "[" << getCurrentTime() << "] [任务调度器] 未创建批量任务" << std::endl;
        }
    }

    // 导出所有任务日志到控制台
    void exportLogs() {
        std::cout << "\n=== 任务执行日志(共" << m_taskLogs.size() << "条)===" << std::endl;
        for (size_t i = 0; i < m_taskLogs.size(); ++i) {
            std::cout << i + 1 << ". " << m_taskLogs[i] << std::endl;
        }
    }

private:
    std::shared_ptr<BatchTaskCommand> m_currentBatchTask;  // 当前批量任务
    std::vector<std::string> m_taskLogs;                   // 任务日志列表
};
步骤 5:客户端测试
int main() {
    // 1. 创建接收者(任务执行器)
    auto backupExecutor = std::make_shared<BackupExecutor>();
    auto syncExecutor = std::make_shared<SyncExecutor>();
    auto logCleanExecutor = std::make_shared<LogCleanExecutor>();

    // 2. 创建任务调度器
    TaskScheduler scheduler;

    // 3. 创建批量任务(执行人:admin)
    auto batchTask = scheduler.createBatchTask("admin");

    // 4. 添加子任务(不同优先级)
    // 数据同步(高优先级)
    auto syncCmd = std::make_shared<SyncCommand>(syncExecutor, "mysql://192.168.1.100:3306/user_db",
                                                "mysql://192.168.1.101:3306/user_db_backup", "admin");
    // 文件备份(中优先级)
    auto backupCmd = std::make_shared<BackupCommand>(backupExecutor, "/data/app/log", "/backup/log/202406",
                                                    "admin", TaskCommand::Priority::MEDIUM);
    // 日志清理(低优先级)
    auto logCleanCmd = std::make_shared<LogCleanCommand>(logCleanExecutor, "/var/log", 7,
                                                        "admin", TaskCommand::Priority::LOW);

    batchTask->addSubCommand(syncCmd);
    batchTask->addSubCommand(backupCmd);
    batchTask->addSubCommand(logCleanCmd);

    // 5. 测试批量执行(按优先级:数据同步→文件备份→日志清理)
    scheduler.executeBatchTask();

    // 6. 测试批量撤销
    scheduler.undoBatchTask();

    // 7. 测试导出日志
    scheduler.exportLogs();

    return 0;
}
运行结果与核心解析
[2024-06-01 15:00:00] [批量任务] 开始执行,共3个子任务(按优先级排序)
[2024-06-01 15:00:00] [数据同步] 开始执行:源数据库:mysql://192.168.1.100:3306/user_db,目标数据库:mysql://192.168.1.101:3306/user_db_backup,执行人:admin
[2024-06-01 15:00:01] [数据同步] 执行成功
[2024-06-01 15:00:01] [文件备份] 开始执行:源路径:/data/app/log,目标路径:/backup/log/202406,执行人:admin
[2024-06-01 15:00:01] [文件备份] 执行成功
[2024-06-01 15:00:01] [日志清理] 开始执行:日志路径:/var/log,保留天数:7,执行人:admin
[2024-06-01 15:00:02] [日志清理] 执行成功
[2024-06-01 15:00:02] [批量任务] 执行完成

[2024-06-01 15:00:02] [批量任务] 开始撤销,共3个子任务
[2024-06-01 15:00:02] [日志清理] 撤销操作:恢复清理的日志(日志路径:/var/log,保留天数:7,执行人:admin)
[2024-06-01 15:00:02] [文件备份] 撤销操作:删除备份文件(源路径:/data/app/log,目标路径:/backup/log/202406,执行人:admin)
[2024-06-01 15:00:02] [数据同步] 撤销操作:回滚同步数据(源数据库:mysql://192.168.1.100:3306/user_db,目标数据库:mysql://192.168.1.101:3306/user_db_backup,执行人:admin)
[2024-06-01 15:00:02] [批量任务] 撤销完成

=== 任务执行日志(共2条)===
1. [2024-06-01 15:00:02] [批量任务] [执行人:admin] [执行状态:成功] [子任务数量:3]
  - [2024-06-01 15:00:02] [任务类型:数据同步] [优先级:高] [执行人:admin] [详情:源数据库:mysql://192.168.1.100:3306/user_db,目标数据库:mysql://192.168.1.101:3306/user_db_backup,执行人:admin] [执行状态:成功]
  - [2024-06-01 15:00:02] [任务类型:文件备份] [优先级:中] [执行人:admin] [详情:源路径:/data/app/log,目标路径:/backup/log/202406,执行人:admin] [执行状态:成功]
  - [2024-06-01 15:00:02] [任务类型:日志清理] [优先级:低] [执行人:admin] [详情:日志路径:/var/log,保留天数:7,执行人:admin] [执行状态:成功]

2. [2024-06-01 15:00:02] [批量任务] [执行人:admin] [执行状态:未执行] [子任务数量:3]
  - [2024-06-01 15:00:02] [任务类型:数据同步] [优先级:高] [执行人:admin] [详情:源数据库:mysql://192.168.1.100:3306/user_db,目标数据库:mysql://192.168.1.101:3306/user_db_backup,执行人:admin] [执行状态:未执行]
  - [2024-06-01 15:00:02] [任务类型:文件备份] [优先级:中] [执行人:admin] [详情:源路径:/data/app/log,目标路径:/backup/log/202406,执行人:admin] [执行状态:未执行]
  - [2024-06-01 15:00:02] [任务类型:日志清理] [优先级:低] [执行人:admin] [详情:日志路径:/var/log,保留天数:7,执行人:admin] [执行状态:未执行]

核心亮点

  • 复合命令:通过BatchTaskCommand组合多个子命令,支持批量执行 / 撤销,类似事务的 “原子性”;
  • 优先级调度:使用优先级队列存储子命令,高优先级任务先执行,符合实际项目需求;
  • 日志审计:每个命令都记录详细日志(时间、执行人、详情、状态),支持导出,便于审计和故障排查;
  • 线程安全:接收者方法加互斥锁,支持多线程环境下的任务执行;
  • 幂等性:命令执行前检查m_isExecuted,避免重复执行导致的业务异常。

四、命令模式的工程化优化:从 “能用” 到 “好用”

上面的案例是基础实现,实际项目中还需要考虑更多工程化细节,以下是 4 个关键优化点:

4.1 命令的序列化与持久化

在分布式系统中,命令需要跨节点传递,或在故障后恢复执行,此时需要将命令序列化(转为 JSON/Protobuf 格式),存储到数据库或文件中。

示例:给TaskCommand添加序列化接口:

class TaskCommand : public Command {
public:
    // 序列化命令(转为字符串,便于存储/传输)
    virtual std::string serialize() const = 0;
    // 反序列化命令(从字符串恢复命令)
    virtual static std::shared_ptr<TaskCommand> deserialize(const std::string& data) = 0;
};

// 具体命令实现序列化(以BackupCommand为例)
std::string BackupCommand::serialize() const {
    // 用JSON格式存储命令信息(实际项目可用nlohmann/json库)
    return R"({
        "type": "BackupCommand",
        "srcPath": ")" + m_srcPath + R"(",
        "destPath": ")" + m_destPath + R"(",
        "operatorName": ")" + m_operatorName + R"(",
        "priority": ")" + (m_priority == Priority::HIGH ? "HIGH" : "MEDIUM") + R"("
    })";
}

std::shared_ptr<TaskCommand> BackupCommand::deserialize(const std::string& data) {
    // 解析JSON数据,创建命令对象
    auto jsonData = nlohmann::json::parse(data);
    std::string srcPath = jsonData["srcPath"];
    std::string destPath = jsonData["destPath"];
    std::string operatorName = jsonData["operatorName"];
    Priority priority = (jsonData["priority"] == "HIGH") ? Priority::HIGH : Priority::MEDIUM;
    auto executor = std::make_shared<BackupExecutor>();
    return std::make_shared<BackupCommand>(executor, srcPath, destPath, operatorName, priority);
}

4.2 命令的超时控制与重试机制

实际任务可能因网络波动、资源不足等原因执行失败,需要添加超时控制和重试机制:

class TaskCommand : public Command {
public:
    TaskCommand(int timeoutMs = 5000, int retryCount = 3)
        : m_timeoutMs(timeoutMs), m_retryCount(retryCount) {}

    void execute() override {
        for (int i = 0; i < m_retryCount; ++i) {
            try {
                // 超时控制(用std::future实现)
                auto future = std::async(std::launch::async, [this]() {
                    doExecute();  // 实际执行逻辑
                });
                if (future.wait_for(std::chrono::milliseconds(m_timeoutMs)) == std::future_status::ready) {
                    m_isExecuted = true;
                    return;
                } else {
                    std::cout << "命令执行超时,重试第" << i+1 << "次" << std::endl;
                }
            } catch (const std::exception& e) {
                std::cout << "命令执行失败:" << e.what() << ",重试第" << i+1 << "次" << std::endl;
            }
        }
        throw std::runtime_error("命令执行失败,已重试" + std::to_string(m_retryCount) + "次");
    }

private:
    virtual void doExecute() = 0;  // 实际执行逻辑
    int m_timeoutMs;               // 超时时间(毫秒)
    int m_retryCount;              // 重试次数
};

4.3 命令的监控与告警

在大型系统中,需要监控命令的执行状态,当命令执行失败或超时,及时触发告警(邮件、短信、钉钉):

class TaskCommand : public Command {
public:
    // 设置告警回调函数
    using AlarmCallback = std::function<void(const std::string& errorMsg)>;
    void setAlarmCallback(AlarmCallback callback) { m_alarmCallback = callback; }

    void execute() override {
        try {
            doExecute();
            m_isExecuted = true;
        } catch (const std::exception& e) {
            std::string errorMsg = "命令执行失败:" + std::string(e.what());
            if (m_alarmCallback) {
                m_alarmCallback(errorMsg);  // 触发告警
            }
            throw;
        }
    }

private:
    AlarmCallback m_alarmCallback;  // 告警回调函数
};

// 客户端使用
auto backupCmd = std::make_shared<BackupCommand>(...);
backupCmd->setAlarmCallback([](const std::string& errorMsg) {
    // 发送告警邮件
    sendEmail("admin@example.com", "命令执行失败告警", errorMsg);
});

4.4 依赖注入(DI)管理接收者

实际项目中,接收者可能有复杂的依赖关系(比如需要数据库连接、配置文件),直接在命令中创建接收者会导致耦合过紧。可以使用依赖注入框架(如 Boost.DI、Google Guice)管理接收者的创建和注入:

// 用依赖注入框架创建接收者
auto injector = di::make_injector(
    di::bind<BackupExecutor>().in(di::singleton)  // 单例模式
);
auto backupExecutor = injector.create<std::shared_ptr<BackupExecutor>>();

// 命令中注入接收者,而非直接创建
auto backupCmd = std::make_shared<BackupCommand>(backupExecutor, srcPath, destPath, operatorName);

五、避坑指南:实际开发中容易踩的 5 个坑

5.1 过度设计,命令类膨胀

:每个简单操作都创建独立的命令类(比如 “灯光调亮 10%”“调亮 20%” 都做单独命令),导致系统中命令类数量暴增,维护成本高。解决方案

  • 合并相似命令:用一个命令类 + 参数区分(比如LightAdjustCommand,通过adjustValue参数控制调亮 / 调暗、百分比);
  • 用函数对象(lambda)简化简单命令:对于无需撤销、日志的简单命令,直接用 lambda 表达式,无需创建类。

5.2 撤销逻辑复杂,状态不一致

:命令之间有依赖关系(比如先创建文件再写入内容),撤销时未按逆序执行,导致状态不一致(比如先撤销创建文件,再撤销写入,此时文件已不存在)。解决方案

  • 撤销时按命令执行的逆序操作(用栈存储已执行命令,撤销时弹出栈顶命令);
  • 命令执行前记录完整的上下文快照(比如文件的原始内容、数据库的事务日志),撤销时直接恢复快照,而非反向执行操作。

5.3 命令与接收者耦合过紧

:命令类直接依赖接收者的具体实现(比如BackupCommand依赖BackupExecutorbackupFile方法),当接收者方法修改时,命令类也需要修改。解决方案

  • 给接收者定义抽象接口(如IBackupExecutor),命令类依赖抽象接口而非具体实现;
  • 采用依赖注入模式,将接收者注入命令类,而非命令类直接创建接收者。

5.4 忽略命令的幂等性

:命令重复执行导致业务异常(比如重复备份文件导致磁盘满、重复扣库存导致数据错误)。解决方案

  • 保证命令的幂等性:同一命令执行多次,结果与执行一次一致(比如备份文件时先判断目标文件是否存在,存在则跳过);
  • 给命令添加唯一标识(UUID),执行前检查该命令是否已执行过(比如存在数据库中),避免重复执行。

5.5 调用者与命令耦合过紧

:调用者直接依赖具体命令类(比如遥控器绑定LightOnCommand),新增命令时需要修改调用者代码。解决方案

  • 调用者依赖抽象命令类,通过配置文件或工厂模式动态创建命令对象(比如配置文件中指定 “按钮 0 绑定 LightOnCommand”,调用者读取配置创建命令);
  • 用策略模式 + 命令模式结合:将命令的创建逻辑封装到工厂类
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值