内容目录

痛点分析:程序员的“背锅血泪史”🩸

你是否经历过这些“代码甩锅现场”?

  • 场景1:用户点了“撤销”按钮,但你的代码像金鱼记忆🐟,完全不知道上一步干了啥!
  • 场景2:想实现“操作宏录制”,结果每个步骤耦合得像502胶水,改一处崩十处💥!
  • 场景3:多线程任务队列里,任务参数传来传去,最后像击鼓传花一样传丢了🥁💐!

灵魂拷问

你的代码是执行命令,还是在玩《烫手山芋》游戏?🎮🔥


解决方案:命令模式——代码界的“甩锅大师”🎩🌀

命令模式(Command)核心思想:

  • 请求对象化:把操作打包成独立对象,像快递包裹一样传递📦🚚!
  • 解耦直通车:调用者、执行者老死不相往来,维护代码像吃蛋糕🍰!
  • 无限回滚:支持撤销/重做/排队,让用户为所欲为(然后反悔)↩️↪️!

适用场景

  • 事务操作(数据库回滚/文件操作)💾↩️
  • 游戏指令系统(技能连招/回放系统)🎮📼
  • GUI操作队列(按钮点击/菜单命令)🖱️📜

手把手教学:从“背锅侠”到“甩锅王”🛡️➡️🎩

Step 1:传统写法——代码の《背锅联盟》

// 直接调用具体对象的方法  
class Light {  
public:  
    void on() { cout << "灯亮了💡" << endl; }  
};  

class RemoteControl {  
public:  
    void pressButton() {  
        light_.on();  // 直接耦合具体对象  
    }  
private:  
    Light light_;  
};  

// 想加个撤销功能?重写吧您嘞!💣  

缺点总结

  • 直接耦合:遥控器和灯绑定得像连体婴👶👶
  • 无法扩展:新增操作要修改调用方,违反开闭原则🚪💥
  • 历史记录? 不存在的,用户别想反悔!😡

Step 2:命令模式——开启“甩锅大法”🌀

① 定义命令接口(甩锅协议)

class Command {  
public:  
    virtual ~Command() = default;  
    virtual void execute() = 0;  
    virtual void undo() = 0;    // 回滚操作  
    virtual string desc() = 0;  // 记录操作描述(方便甩锅)  
};  

② 实现具体命令(甩锅包裹)

// 开灯命令  
class LightOnCommand : public Command {  
public:  
    explicit LightOnCommand(Light& light) : light_(light) {}  

    void execute() override {  
        prev_state_ = light_.isOn();  
        light_.on();  
    }  

    void undo() override {  
        if (!prev_state_) light_.off();  
    }  

    string desc() override { return "开灯命令"; }  

private:  
    Light& light_;  
    bool prev_state_;  // 记录状态用于回滚  
};  

③ 实现调用者(甩锅高手)

class RemoteControl {  
public:  
    void setCommand(unique_ptr cmd) {  
        current_cmd_ = move(cmd);  
    }  

    void pressButton() {  
        if (current_cmd_) {  
            history_.push(current_cmd_->desc());  
            current_cmd_->execute();  
        }  
    }  

    void pressUndo() {  
        if (!history_.empty()) {  
            current_cmd_->undo();  
            history_.pop();  
        }  
    }  

private:  
    unique_ptr current_cmd_;  
    stack history_;  // 记录操作历史  
};  

④ 使用示例——优雅甩锅!

Light light;  
auto cmd = make_unique(light);  
RemoteControl remote;  

remote.setCommand(move(cmd));  
remote.pressButton();  // 灯亮💡  
remote.pressUndo();    // 灯灭🌑  

技术深挖

  • 智能指针管理:用unique_ptr明确命令对象所有权,防止内存泄漏💀
  • 引用传递:命令对象持有具体执行者的引用(而非实例),避免不必要的拷贝🚀
  • 历史堆栈:用stack实现无限撤销(理论上),但记得内存限制🚧

高级技巧:命令模式の“黑暗兵法”🌑⚡

技巧1:宏命令(批量甩锅)

class MacroCommand : public Command {  
public:  
    void addCommand(unique_ptr cmd) {  
        commands_.push_back(move(cmd));  
    }  

    void execute() override {  
        for (auto& cmd : commands_) cmd->execute();  
    }  

    void undo() override {  
        for (auto it = commands_.rbegin(); it != commands_.rend(); ++it)  
            (*it)->undo();  
    }  

private:  
    vector> commands_;  
};  

// 使用:一键完成开灯+开空调+播放音乐  
auto macro = make_unique();  
macro->addCommand(make_unique(light));  
macro->addCommand(make_unique(ac));  
remote.setCommand(move(macro));  

技巧2:C++11的std::function

// 用函数对象替代继承(轻量级命令)  
using CommandFunc = function;  

class FunctionalCommand {  
public:  
    FunctionalCommand(CommandFunc do_cmd, CommandFunc undo_cmd)  
        : do_(do_cmd), undo_(undo_cmd) {}  

    void execute() { do_(); }  
    void undo() { undo_(); }  

private:  
    CommandFunc do_;  
    CommandFunc undo_;  
};  

// 使用Lambda定义命令  
auto cmd = FunctionalCommand(  
    [] { cout << "执行操作" << endl; },  
    [] { cout << "撤销操作" << endl; }  
);  

技巧3:线程安全命令队列

#include   
#include   

class ThreadSafeCommandQueue {  
public:  
    void pushCommand(unique_ptr cmd) {  
        lock_guard lock(mtx_);  
        queue_.push(move(cmd));  
    }  

    void processCommands() {  
        lock_guard lock(mtx_);  
        while (!queue_.empty()) {  
            auto cmd = move(queue_.front());  
            queue_.pop();  
            cmd->execute();  
        }  
    }  

private:  
    queue> queue_;  
    mutex mtx_;  
};  

避坑指南:命令模式的“甩锅翻车”⚠️🚗

  1. 过度封装:简单操作也包装成命令?代码变俄罗斯套娃🪆💣
  2. 资源泄漏:命令对象持有资源忘记释放?智能指针救场!🧠🔒
  3. 线程安全:多线程访问命令队列要加锁,否则数据竞争教你做人🤯🔀
  4. 撤销复杂度:部分操作难以回滚(如网络请求),需设计补偿机制📡💸

评论区互动

💬 “你用命令模式甩过哪些锅?”

  • A:我用命令队列处理网络包,结果队列积压到内存爆炸💥📦
  • B:求问!命令模式 vs 策略模式,区别是啥?(送命题🤯)
  • C:上次用Lambda实现命令,现在代码自己学会甩锅了…🤖🎩

福利时间
🎁 点赞+留言,抽3位送《命令模式防甩锅手册》电子书!(附赠“代码免责声明”模板📜)


结语

命令模式,让代码从“背锅侠”变身“甩锅大师”——你的操作,收放自如! 🎮↩️

转发本文并配文“我的代码已学会甩锅大法!”,截图私信领《C++设计模式:从背锅到封神》终极秘籍!📖


技术深度总结

  • 🎮 模式本质:将请求封装为对象,实现调用者与执行者解耦
  • ⚙️ C++特色:智能指针、Lambda、线程库强化模式实用性
  • 🔄 扩展机制:宏命令、函数对象支持灵活组合
  • 🛡️ 防御编程:通过RAII和锁保障资源与线程安全
dastudio

By dastudio

You are not special.

发表评论