设计模式(C++)详解——命令模式(2)

第一章:缘起------一个程序员的"后悔药"梦想

🎭 场景设定:深夜两点,程序员小明正在疯狂coding,一不小心按下了Ctrl+A然后Delete... "啊!我的代码!" 这一刻,他多么希望有个"后悔药"啊!

1.1 那些年,我们追悔莫及的操作

还记得第一次使用Word时,不小心删除了整段文字的那种绝望吗?或者在玩《我的世界》时,手滑拆掉了辛苦建造的房子?这些痛苦的经历催生了一个伟大的需求:我们需要撤销功能!

但实现撤销功能可不是简单的事。想象一下,如果每个操作都要保存整个文档的状态,你的内存很快就会像春运的火车站一样拥挤。这时候,命令模式就像一位智慧的魔法师,挥舞着魔杖说:"别担心,我有办法!"

1.2 命令模式的"前世今生"

命令模式最早出现在1994年,由四位软件界的"摇滚明星"------Gamma、Helm、Johnson和Vlissides(合称GoF)在他们的经典著作中提出。这就像音乐界的披头士,设计模式界的"Fab Four"!

有趣的时间线:

  • 1994年:命令模式正式"出道"
  • 2000年:成为GUI框架的"标配明星"
  • 2010年:在游戏开发中大放异彩
  • 2020年:在微服务架构中焕发第二春

第二章:命令模式的"魔法学校"

2.1 五个关键角色:命令模式的"复仇者联盟"

想象一下,命令模式就像一场精彩的戏剧,有五个关键角色:
客户 +下订单() 服务员 -订单: 命令 +记下订单() +喊单() <<抽象>> 订单接口 +做菜() +退菜() 具体订单 -厨师: 厨房 -菜品详情 +做菜() +退菜() 厨房 +炒菜() +回收食材()

角色介绍(戏剧版):

角色 剧中身份 现实对应 个性特点
命令(Command) 订单模板 接口/抽象类 严格的"领导",只定规矩不干活
具体命令(ConcreteCommand) 具体订单 实现类 勤劳的"传令兵",知道找谁干活
客户端(Client) 顾客 应用程序 "金主爸爸",提需求的人
调用者(Invoker) 服务员 按钮/菜单 灵活的"协调员",不干活但指挥别人
接收者(Receiver) 厨师 业务对象 真正的"实干家",默默完成所有工作

2.2 魔法咒语:命令模式的三大核心原则

原则一:封装请求

"不要直接告诉厨师怎么做菜,写张订单给他!"

原则二:解耦发送者和接收者

"顾客不需要知道厨师的手机号,通过服务员传话就行!"

原则三:支持高级操作

"可以取消订单、重复下单,甚至来个套餐组合!"

第三章:实战演练------文本编辑器的"时光机"

3.1 需求分析:我们要建造什么样的"时光机"?

想象一下,我们要开发一个超级文本编辑器,具备以下超能力:

  • ✅ 可以撤销任何操作(就像Ctrl+Z)
  • ✅ 可以重做被撤销的操作(就像Ctrl+Y)
  • ✅ 支持操作历史记录
  • ✅ 甚至可以实现"宏命令"(一键完成多个操作)

3.2 代码实现:一步步建造我们的"时光机"

第一步:创建"文档"类(接收者)
cpp 复制代码
/**
 * @brief 文档类 - 文本编辑器的"画布"
 * 
 * 这个类就像画家的画布,负责实际的内容操作。
 * 想象一下,这就是你的Word文档本体!
 */
class 魔法文档 {
private:
    std::string 内容_;        // 文档内容
    size_t 光标位置_;         // 当前光标位置
    
public:
    魔法文档() : 内容_(""), 光标位置_(0) {
        std::cout << "🎨 创建了一个全新的魔法画布!" << std::endl;
    }
    
    /**
     * @brief 插入文本 - 就像用画笔在画布上作画
     * 
     * @param 文字 要插入的魔法文字
     */
    void 插入文字(const std::string& 文字) {
        内容_.insert(光标位置_, 文字);
        光标位置_ += 文字.length();
        std::cout << "✏️ 插入了文字: \"" << 文字 
                  << "\",现在内容是: \"" << 内容_ << "\"" << std::endl;
    }
    
    /**
     * @brief 删除文本 - 就像用橡皮擦擦除画作
     * 
     * @param 长度 要擦除的字符数
     * @return 被擦除的文字(留着以后可能要用)
     */
    std::string 删除文字(size_t 长度) {
        // 确保不会擦除过头
        if (光标位置_ < 长度) {
            长度 = 光标位置_;
        }
        
        size_t 起始位置 = 光标位置_ - 长度;
        std::string 被删除的文字 = 内容_.substr(起始位置, 长度);
        内容_.erase(起始位置, 长度);
        光标位置_ = 起始位置;
        
        std::cout << "🧽 擦除了文字: \"" << 被删除的文字 
                  << "\",现在内容是: \"" << 内容_ << "\"" << std::endl;
        return 被删除的文字;
    }
    
    // 其他魔法方法...
    void 移动光标(size_t 位置) {
        光标位置_ = 位置;
        std::cout << "🔍 光标移动到了位置: " << 位置 << std::endl;
    }
    
    std::string 获取内容() const { return 内容_; }
};
第二步:创建"命令"接口和具体命令
cpp 复制代码
/**
 * @brief 命令接口 - 所有魔法指令的"祖宗"
 * 
 * 这就像魔法世界的基本咒语格式,所有具体咒语都要遵循这个格式。
 */
class 魔法命令 {
public:
    virtual ~魔法命令() = default;
    
    /**
     * @brief 执行咒语 - 让魔法生效!
     */
    virtual void 执行() = 0;
    
    /**
     * @brief 撤销咒语 - 时光倒流!
     */
    virtual void 撤销() = 0;
    
    /**
     * @brief 获取咒语描述 - 告诉别人这是什么魔法
     */
    virtual std::string 获取描述() const = 0;
};

/**
 * @brief 插入文字命令 - 具体的"书写咒语"
 * 
 * 这个咒语专门负责在文档上写入文字。
 */
class 书写咒语 : public 魔法命令 {
private:
    魔法文档& 文档_;      // 要对哪个文档施法
    std::string 文字_;    // 要写入什么文字
    size_t 位置_;         // 在什么位置写入
    
public:
    /**
     * @brief 构造函数 - 准备咒语材料
     */
    书写咒语(魔法文档& 文档, const std::string& 文字) 
        : 文档_(文档), 文字_(文字), 位置_(文档.获取光标位置()) {
        std::cout << "📖 准备书写咒语: \"" << 文字 << "\"" << std::endl;
    }
    
    void 执行() override {
        文档_.移动光标(位置_);
        文档_.插入文字(文字_);
        std::cout << "✨ 书写咒语生效了!" << std::endl;
    }
    
    void 撤销() override {
        文档_.移动光标(位置_);
        文档_.删除文字(文字_.length());
        文档_.移动光标(位置_); // 把光标移回原处
        std::cout << "⏪ 书写咒语被撤销了!" << std::endl;
    }
    
    std::string 获取描述() const override {
        return "书写咒语: \"" + 文字_ + "\"";
    }
};

/**
 * @brief 删除文字命令 - 具体的"清除咒语"
 * 
 * 这个咒语专门负责清除文档上的文字。
 */
class 清除咒语 : public 魔法命令 {
private:
    魔法文档& 文档_;
    size_t 长度_;
    std::string 被删除的文字_;  // 记住删除了什么,以便撤销
    size_t 位置_;
    
public:
    清除咒语(魔法文档& 文档, size_t 长度) 
        : 文档_(文档), 长度_(长度), 位置_(文档.获取光标位置()) {
        std::cout << "🧹 准备清除咒语,长度: " << 长度 << std::endl;
    }
    
    void 执行() override {
        文档_.移动光标(位置_);
        被删除的文字_ = 文档_.删除文字(长度_);
        std::cout << "✨ 清除咒语生效了!" << std::endl;
    }
    
    void 撤销() override {
        文档_.移动光标(位置_);
        文档_.插入文字(被删除的文字_);
        std::cout << "⏪ 清除咒语被撤销了!" << std::endl;
    }
    
    std::string 获取描述() const override {
        return "清除咒语: \"" + 被删除的文字_ + "\"";
    }
};
第三步:创建"命令历史"管理器(调用者)
cpp 复制代码
/**
 * @brief 命令历史 - 魔法世界的"时光机器"
 * 
 * 这个类就像一台时光机,记录所有操作,支持前进和后退。
 */
class 时光机器 {
private:
    std::stack<std::unique_ptr<魔法命令>> 撤销栈_;  // 已经执行的咒语
    std::stack<std::unique_ptr<魔法命令>> 重做栈_;  // 已经撤销的咒语
    
public:
    /**
     * @brief 执行新咒语 - 让时光向前流动
     * 
     * @param 咒语 要执行的新咒语
     */
    void 执行咒语(std::unique_ptr<魔法命令> 咒语) {
        咒语->执行();
        撤销栈_.push(std::move(咒语));
        
        // 执行新咒语时,清空重做栈(就像改变了时间线)
        while (!重做栈_.empty()) {
            重做栈_.pop();
        }
        
        std::cout << "⏩ 新咒语已加入时间线!" << std::endl;
        this->显示时间线状态();
    }
    
    /**
     * @brief 撤销咒语 - 让时光倒流一步
     * 
     * @return 成功撤销返回true,否则false
     */
    bool 撤销() {
        if (撤销栈_.empty()) {
            std::cout << "🚫 无法撤销,已经是最早的时间点了!" << std::endl;
            return false;
        }
        
        auto 咒语 = std::move(撤销栈_.top());
        撤销栈_.pop();
        
        咒语->撤销();
        重做栈_.push(std::move(咒语));
        
        std::cout << "⏪ 时光倒流一步!" << std::endl;
        this->显示时间线状态();
        return true;
    }
    
    /**
     * @brief 重做咒语 - 让时光重新前进
     * 
     * @return 成功重做返回true,否则false
     */
    bool 重做() {
        if (重做栈_.empty()) {
            std::cout << "🚫 无法重做,已经是最新的时间点了!" << std::endl;
            return false;
        }
        
        auto 咒语 = std::move(重做栈_.top());
        重做栈_.pop();
        
        咒语->执行();
        撤销栈_.push(std::move(咒语));
        
        std::cout << "⏩ 时光重新前进!" << std::endl;
        this->显示时间线状态();
        return true;
    }
    
    /**
     * @brief 显示当前时间线状态
     */
    void 显示时间线状态() const {
        std::cout << "📊 时间线状态 === "
                  << "撤销栈大小: " << 撤销栈_.size()
                  << ", 重做栈大小: " << 重做栈_.size() 
                  << std::endl;
    }
};
第四步:客户端代码 - 开始我们的魔法表演!
cpp 复制代码
/**
 * @brief 主函数 - 魔法表演的舞台
 * 
 * 这里我们将演示命令模式的完整魔法效果!
 */
int main() {
    std::cout << "🎪 欢迎来到命令模式魔法秀!" << std::endl;
    std::cout << "=========================================" << std::endl;
    
    // 创建我们的魔法画布
    魔法文档 我的画布;
    时光机器 我的时光机;
    
    std::cout << "\n🌟 第一幕:开始创作之旅" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 执行一系列创作咒语
    我的时光机.执行咒语(std::make_unique<书写咒语>(我的画布, "你好"));
    我的时光机.执行咒语(std::make_unique<书写咒语>(我的画布, ",魔法世界"));
    我的时光机.执行咒语(std::make_unique<书写咒语>(我的画布, "!"));
    
    std::cout << "\n📝 当前画布内容: \"" << 我的画布.获取内容() << "\"" << std::endl;
    
    std::cout << "\n🔄 第二幕:体验时光倒流" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 测试撤销功能(就像按Ctrl+Z)
    我的时光机.撤销();
    std::cout << "撤销后内容: \"" << 我的画布.获取内容() << "\"" << std::endl;
    
    我的时光机.撤销();
    std::cout << "再次撤销后内容: \"" << 我的画布.获取内容() << "\"" << std::endl;
    
    std::cout << "\n⚡ 第三幕:体验时光前进" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 测试重做功能(就像按Ctrl+Y)
    我的时光机.重做();
    std::cout << "重做后内容: \"" << 我的画布.获取内容() << "\"" << std::endl;
    
    std::cout << "\n🎭 第四幕:混合魔法表演" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 来点复杂的操作组合
    我的时光机.执行咒语(std::make_unique<清除咒语>(我的画布, 2));
    std::cout << "清除后内容: \"" << 我的画布.获取内容() << "\"" << std::endl;
    
    // 再次撤销看看
    我的时光机.撤销();
    std::cout << "最终内容: \"" << 我的画布.获取内容() << "\"" << std::endl;
    
    std::cout << "\n🎉 魔法表演圆满结束!" << std::endl;
    std::cout << "=========================================" << std::endl;
    
    return 0;
}

3.3 运行结果:观看我们的魔法秀

编译运行上面的代码,你会看到一场精彩的"魔法表演":

bash 复制代码
# 编译命令
g++ -std=c++17 -o 魔法编辑器 魔法编辑器.cpp

# 运行结果
🎪 欢迎来到命令模式魔法秀!
=========================================

🌟 第一幕:开始创作之旅
-----------------------------------------
🎨 创建了一个全新的魔法画布!
📖 准备书写咒语: "你好"
🔍 光标移动到了位置: 0
✏️ 插入了文字: "你好",现在内容是: "你好"
✨ 书写咒语生效了!
⏩ 新咒语已加入时间线!
📊 时间线状态 === 撤销栈大小: 1, 重做栈大小: 0

# ... 更多精彩的输出 ...

第四章:进阶魔法------智能家居的"情景模式"

4.1 现实需求:懒人必备的智能家居

想象一下这样的场景:

  • 🏠 回家模式:开门瞬间,灯光亮起、空调启动、音乐播放
  • 🎬 影院模式:一键关闭灯光、拉上窗帘、打开投影仪
  • 🛌 睡眠模式:逐渐调暗灯光、关闭电器、设置空调温度

这些"情景模式"就是命令模式的完美应用场景!

4.2 代码实现:打造智能家居控制系统

智能设备类(接收者们)
cpp 复制代码
/**
 * @brief 智能灯光 - 会变色的魔法灯泡
 */
class 智能灯光 {
private:
    bool 开关状态_ = false;
    int 亮度_ = 100;
    std::string 颜色_ = "白色";
    
public:
    void 开启() {
        开关状态_ = true;
        std::cout << "💡 灯光已开启,亮度: " << 亮度_ << "%,颜色: " << 颜色_ << std::endl;
    }
    
    void 关闭() {
        开关状态_ = false;
        std::cout << "🌙 灯光已关闭" << std::endl;
    }
    
    void 设置亮度(int 亮度) {
        亮度_ = 亮度;
        if (开关状态_) {
            std::cout << "🔅 灯光亮度调整为: " << 亮度_ << "%" << std::endl;
        }
    }
    
    void 设置颜色(const std::string& 颜色) {
        颜色_ = 颜色;
        if (开关状态_) {
            std::cout << "🎨 灯光颜色变为: " << 颜色_ << std::endl;
        }
    }
};

/**
 * @brief 智能空调 - 懂你的温度管家
 */
class 智能空调 {
private:
    bool 开关状态_ = false;
    int 温度_ = 26;
    std::string 模式_ = "自动";
    
public:
    void 开启() {
        开关状态_ = true;
        std::cout << "❄️ 空调已开启,温度: " << 温度_ << "℃,模式: " << 模式_ << std::endl;
    }
    
    void 关闭() {
        开关状态_ = false;
        std::cout << "☀️ 空调已关闭" << std::endl;
    }
    
    void 设置温度(int 温度) {
        温度_ = 温度;
        if (开关状态_) {
            std::cout << "🌡️ 空调温度设置为: " << 温度_ << "℃" << std::endl;
        }
    }
    
    void 设置模式(const std::string& 模式) {
        模式_ = 模式;
        if (开关状态_) {
            std::cout << "🌀 空调模式改为: " << 模式_ << std::endl;
        }
    }
};

/**
 * @brief 智能窗帘 - 自动开合的魔法窗帘
 */
class 智能窗帘 {
private:
    bool 开关状态_ = false;  // true表示打开,false表示关闭
    int 开合程度_ = 0;       // 0-100表示开合百分比
    
public:
    void 打开() {
        开关状态_ = true;
        开合程度_ = 100;
        std::cout << "🪟 窗帘已完全打开" << std::endl;
    }
    
    void 关闭() {
        开关状态_ = false;
        开合程度_ = 0;
        std::cout << "🚪 窗帘已完全关闭" << std::endl;
    }
    
    void 设置开合(int 程度) {
        开合程度_ = 程度;
        开关状态_ = (程度 > 0);
        std::cout << "📏 窗帘开合程度设置为: " << 开合程度_ << "%" << std::endl;
    }
};
情景模式命令(宏命令)
cpp 复制代码
/**
 * @brief 情景模式命令 - 一键执行多个操作的"组合咒语"
 * 
 * 这就像魔法世界的高级复合咒语,一个咒语包含多个效果。
 */
class 回家模式命令 : public 魔法命令 {
private:
    智能灯光& 灯光_;
    智能空调& 空调_;
    智能窗帘& 窗帘_;
    
public:
    回家模式命令(智能灯光& 灯, 智能空调& 空, 智能窗帘& 窗)
        : 灯光_(灯), 空调_(空), 窗帘_(窗) {}
    
    void 执行() override {
        std::cout << "\n🏠 启动回家模式!" << std::endl;
        std::cout << "=======================" << std::endl;
        
        灯光_.开启();
        灯光_.设置亮度(80);
        灯光_.设置颜色("暖黄色");
        
        空调_.开启();
        空调_.设置温度(24);
        空调_.设置模式("舒适");
        
        窗帘_.打开();
        
        std::cout << "=======================" << std::endl;
        std::cout << "🎉 回家模式设置完成!" << std::endl;
    }
    
    void 撤销() override {
        std::cout << "\n🔄 撤销回家模式..." << std::endl;
        灯光_.关闭();
        空调_.关闭();
        窗帘_.关闭();
    }
    
    std::string 获取描述() const override {
        return "回家模式(灯光+空调+窗帘)";
    }
};

class 影院模式命令 : public 魔法命令 {
private:
    智能灯光& 灯光_;
    智能窗帘& 窗帘_;
    
public:
    影院模式命令(智能灯光& 灯, 智能窗帘& 窗)
        : 灯光_(灯), 窗帘_(窗) {}
    
    void 执行() override {
        std::cout << "\n🎬 启动影院模式!" << std::endl;
        std::cout << "=======================" << std::endl;
        
        灯光_.设置亮度(10);
        灯光_.设置颜色("蓝色");
        
        窗帘_.关闭();
        
        std::cout << "🍿 准备好爆米花,电影开始!" << std::endl;
        std::cout << "=======================" << std::endl;
    }
    
    void 撤销() override {
        std::cout << "\n🔄 撤销影院模式..." << std::endl;
        灯光_.设置亮度(100);
        灯光_.设置颜色("白色");
        窗帘_.打开();
    }
    
    std::string 获取描述() const override {
        return "影院模式(调暗灯光+关闭窗帘)";
    }
};
智能遥控器(调用者)
cpp 复制代码
/**
 * @brief 智能遥控器 - 情景模式的指挥中心
 * 
 * 就像魔法师的魔杖,一挥就能触发复杂的魔法效果。
 */
class 智能遥控器 {
private:
    std::vector<std::unique_ptr<魔法命令>> 按钮_;  // 每个按钮对应一个情景模式
    
public:
    /**
     * @brief 设置按钮功能
     * 
     * @param 按钮编号 哪个按钮(从0开始)
     * @param 命令 要执行的命令
     */
    void 设置按钮(int 按钮编号, std::unique_ptr<魔法命令> 命令) {
        if (按钮编号 >= 按钮_.size()) {
            按钮_.resize(按钮编号 + 1);
        }
        按钮_[按钮编号] = std::move(命令);
        std::cout << "🔘 按钮" << 按钮编号 << "设置完成!" << std::endl;
    }
    
    /**
     * @brief 按下按钮 - 触发情景模式
     * 
     * @param 按钮编号 按哪个按钮
     */
    void 按下按钮(int 按钮编号) {
        if (按钮编号 < 按钮_.size() && 按钮_[按钮编号]) {
            std::cout << "\n👆 按下按钮" << 按钮编号 << "..." << std::endl;
            按钮_[按钮编号]->执行();
        } else {
            std::cout << "❌ 按钮" << 按钮编号 << "未设置功能!" << std::endl;
        }
    }
    
    /**
     * @brief 显示所有按钮功能
     */
    void 显示按钮功能() {
        std::cout << "\n📋 遥控器按钮功能列表:" << std::endl;
        for (size_t i = 0; i < 按钮_.size(); ++i) {
            if (按钮_[i]) {
                std::cout << "  按钮" << i << ": " << 按钮_[i]->获取描述() << std::endl;
            }
        }
    }
};
客户端演示代码
cpp 复制代码
/**
 * @brief 智能家居演示 - 体验未来生活
 */
void 演示智能家居() {
    std::cout << "\n🤖 智能家居系统启动中..." << std::endl;
    std::cout << "=========================================" << std::endl;
    
    // 创建智能设备
    智能灯光 客厅灯;
    智能空调 客厅空调;
    智能窗帘 客厅窗帘;
    
    // 创建智能遥控器
    智能遥控器 我的遥控器;
    时光机器 家居时光机;
    
    // 设置情景模式
    我的遥控器.设置按钮(0, 
        std::make_unique<回家模式命令>(客厅灯, 客厅空调, 客厅窗帘));
    
    我的遥控器.设置按钮(1, 
        std::make_unique<影院模式命令>(客厅灯, 客厅窗帘));
    
    // 显示功能列表
    我的遥控器.显示按钮功能();
    
    std::cout << "\n🏠 场景一:下班回家" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 执行回家模式
    家居时光机.执行咒语(std::make_unique<回家模式命令>(客厅灯, 客厅空调, 客厅窗帘));
    
    std::cout << "\n⏸️ 休息一会儿..." << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    std::cout << "\n🎬 场景二:家庭影院时间" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 执行影院模式
    家居时光机.执行咒语(std::make_unique<影院模式命令>(客厅灯, 客厅窗帘));
    
    std::cout << "\n🔄 场景三:突然想撤销影院模式" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
    
    // 撤销影院模式
    家居时光机.撤销();
    
    std::cout << "\n🔚 演示结束,关闭所有设备" << std::endl;
    std::cout << "=========================================" << std::endl;
}

第五章:命令模式的"超能力"解析

5.1 八大优势:为什么命令模式这么"香"?

超能力 具体表现 现实比喻
🔗 解耦魔法 发送者和接收者完全独立 顾客不需要认识厨师
⏪ 时光倒流 完美支持撤销/重做 写作文时的"撤销"按钮
📚 操作日志 可以记录所有操作历史 飞机的黑匣子
👥 任务队列 支持操作排队执行 银行取号排队系统
🎯 宏命令 组合多个操作为一个 电脑的一键清理
⚡ 异步执行 支持后台执行命令 外卖订单后台处理
🎭 事务处理 要么全成功要么全失败 银行转账事务
🔧 动态配置 运行时改变命令绑定 游戏手柄按键自定义

5.2 三大应用场景:命令模式的"主战场"

场景一:GUI应用程序

  • 菜单项、工具栏按钮、快捷键
  • 每个UI控件都绑定一个命令对象
  • 支持统一的撤销/重做系统

场景二:游戏开发

  • 玩家操作记录(用于回放功能)
  • AI行为调度系统
  • 游戏状态保存/加载

场景三:事务系统

  • 数据库事务管理
  • 微服务架构中的 Saga 模式
  • 分布式系统中的操作日志

第六章:实战技巧与最佳实践

6.1 性能优化:让命令模式"飞起来"

技巧一:使用对象池

cpp 复制代码
// 命令对象池 - 避免频繁创建销毁
class 命令对象池 {
private:
    std::stack<std::unique_ptr<魔法命令>> 池子_;
    
public:
    template<typename T, typename... Args>
    std::unique_ptr<T> 获取命令(Args&&... args) {
        if (池子_.empty()) {
            return std::make_unique<T>(std::forward<Args>(args)...);
        }
        auto 命令 = std::move(池子_.top());
        池子_.pop();
        return std::unique_ptr<T>(static_cast<T*>(命令.release()));
    }
    
    void 归还命令(std::unique_ptr<魔法命令> 命令) {
        池子_.push(std::move(命令));
    }
};

技巧二:懒加载状态

cpp 复制代码
// 只在需要时加载状态,节省内存
class 大文件操作命令 : public 魔法命令 {
private:
    std::string 文件名_;
    mutable std::unique_ptr<文件状态> 状态_;  // 延迟加载
    
    void 确保状态已加载() const {
        if (!状态_) {
            状态_ = std::make_unique<文件状态>(文件名_);
        }
    }
    
public:
    void 撤销() override {
        确保状态已加载();  // 只在撤销时加载状态
        // ... 撤销操作
    }
};

6.2 设计模式组合:命令模式的"好搭档"

组合一:命令模式 + 组合模式 = 宏命令系统

cpp 复制代码
// 组合命令 - 可以包含其他命令的超级命令
class 组合命令 : public 魔法命令 {
private:
    std::vector<std::unique_ptr<魔法命令>> 子命令列表_;
    
public:
    void 添加命令(std::unique_ptr<魔法命令> 命令) {
        子命令列表_.push_back(std::move(命令));
    }
    
    void 执行() override {
        for (auto& 命令 : 子命令列表_) {
            命令->执行();
        }
    }
    
    void 撤销() override {
        // 按相反顺序撤销
        for (auto it = 子命令列表_.rbegin(); it != 子命令列表_.rend(); ++it) {
            (*it)->撤销();
        }
    }
};

组合二:命令模式 + 备忘录模式 = 完美撤销系统

cpp 复制代码
// 备忘录模式保存状态
class 文档备忘录 {
private:
    std::string 内容_;
    size_t 光标位置_;
    
public:
    文档备忘录(const std::string& 内容, size_t 位置) 
        : 内容_(内容), 光标位置_(位置) {}
    
    std::string 获取内容() const { return 内容_; }
    size_t 获取光标位置() const { return 光标位置_; }
};

// 支持备忘录的命令
class 支持备忘录的命令 : public 魔法命令 {
protected:
    virtual 文档备忘录 创建备忘录() const = 0;
    virtual void 从备忘录恢复(const 文档备忘录&) = 0;
};

第七章:现代框架中的命令模式

7.1 Qt框架:QUndoCommand的强大威力

cpp 复制代码
// Qt中的命令模式实现
class 我的撤销命令 : public QUndoCommand {
public:
    我的撤销命令(文档类* 文档, const QString& 旧文本, const QString& 新文本)
        : 文档指针_(文档), 旧文本_(旧文本), 新文本_(新文本) {}
    
    void undo() override {
        文档指针_->设置文本(旧文本_);
        setText(QString("撤销: 文本修改"));
    }
    
    void redo() override {
        文档指针_->设置文本(新文本_);
        setText(QString("重做: 文本修改"));
    }
    
private:
    文档类* 文档指针_;
    QString 旧文本_;
    QString 新文本_;
};

// 使用示例
QUndoStack* 撤销栈 = new QUndoStack(this);
撤销栈->push(new 我的撤销命令(文档, "旧内容", "新内容"));

7.2 游戏开发:Unity中的命令模式应用

csharp 复制代码
// Unity C# 中的命令模式
public interface I命令 {
    void 执行();
    void 撤销();
}

public class 移动命令 : I命令 {
    private Transform 目标;
    private Vector3 移动方向;
    private float 移动距离;
    
    public 移动命令(Transform 目标物体, Vector3 方向, float 距离) {
        目标 = 目标物体;
        移动方向 = 方向;
        移动距离 = 距离;
    }
    
    public void 执行() {
        目标.position += 移动方向 * 移动距离;
    }
    
    public void 撤销() {
        目标.position -= 移动方向 * 移动距离;
    }
}

// 命令管理器
public class 命令管理器 : MonoBehaviour {
    private Stack<I命令> 命令历史 = new Stack<I命令>();
    
    public void 执行命令(I命令 命令) {
        命令.执行();
        命令历史.Push(命令);
    }
    
    public void 撤销命令() {
        if (命令历史.Count > 0) {
            I命令 命令 = 命令历史.Pop();
            命令.撤销();
        }
    }
}

第八章:命令模式的未来展望

8.1 微服务架构:命令查询职责分离(CQRS)

在现代微服务架构中,命令模式演变成了CQRS模式:
客户端 命令端 查询端 命令处理器 领域模型 事件存储 查询数据库 查询结果

8.2 事件溯源:永久的操作记录

事件溯源是命令模式的终极形态:

  • 每个命令产生一个事件
  • 所有事件被永久存储
  • 可以通过重放事件重建任何时间点的状态
cpp 复制代码
// 事件溯源示例
class 银行账户 {
private:
    std::vector<std::unique_ptr<账户事件>> 事件流_;
    int 当前余额_ = 0;
    
public:
    void 执行命令(std::unique_ptr<账户命令> 命令) {
        auto 事件 = 命令->执行(当前余额_);
        应用事件(事件.get());
        事件流_.push_back(std::move(事件));
    }
    
    void 应用事件(账户事件* 事件) {
        // 根据事件更新状态
        当前余额_ = 事件->应用到(当前余额_);
    }
    
    // 从事件流重建状态
    void 从事件流重建() {
        当前余额_ = 0;
        for (auto& 事件 : 事件流_) {
            应用事件(事件.get());
        }
    }
};

总结:命令模式的魔法启示

命令模式就像软件工程中的"时间魔法",它给了我们三个超能力:

🎯 核心价值

  1. 解耦的智慧:让请求发送者和接收者各自独立,系统更灵活
  2. 时间的掌控:完美支持撤销/重做,再也不怕手滑
  3. 组合的威力:宏命令让复杂操作变得简单

🚀 实用建议

  • 适合场景:需要撤销/重做、任务队列、操作日志的系统
  • 避免过度:简单操作不要强行用命令模式
  • 性能考量:注意命令对象的创建和内存使用

🌟 未来展望

命令模式从简单的GUI撤销功能,发展到现代的事件溯源、CQRS架构,证明了其强大的生命力和适应性。随着云原生、微服务架构的普及,命令模式的思想将在分布式系统中继续发挥重要作用。


最后的小故事:还记得开头的小明吗?他学习了命令模式后,不仅实现了完美的撤销功能,还把整个编辑器的架构重构得更加灵活。现在,他再也不用担心误操作了,甚至还能给用户提供操作历史记录功能。最重要的是------他终于可以安心睡觉了!

命令模式就是这样一种魔法:它不仅能解决技术问题,还能给程序员带来心灵的宁静。希望这次奇妙的命令模式之旅,能让你真正爱上这个强大的设计模式!


<摘要>

命令模式就像软件世界的"时间魔法",通过将操作封装成对象,实现了发送者和接收者的解耦,完美支持撤销/重做、操作日志、任务队列等高级功能。本文通过生动的故事和丰富的实例,从文本编辑器到智能家居,全面展示了命令模式的强大威力和实际应用。


<解析>

本文以轻松幽默的讲故事方式,深入浅出地解析了命令模式的各个方面。通过"魔法咒语"的比喻、丰富的代码实例、直观的图表展示,让读者在愉快的阅读体验中掌握命令模式的核心概念和实践技巧。从基础实现到高级应用,从性能优化到现代框架集成,全面覆盖了命令模式的知识体系。

相关推荐
new_daimond2 小时前
设计模式-原型模式详解
设计模式·原型模式
敲上瘾2 小时前
HTTP协议工作原理与生产环境服务器搭建实战
服务器·网络·c++·网络协议·http
Zfox_2 小时前
【C++项目】微服务即时通讯系统:服务端
数据库·c++·微服务·中间件·rpc·架构·即时通讯
清朝牢弟3 小时前
基于Win系统下PCL库入门到实践:IO模块之PCD文件的读写(附详细代码)
c++·pcl·pcd
爱和冰阔落3 小时前
【C++STL详解】带头双向循环结构 + 双向迭代器,核心接口 + 排序效率 + 避坑指南
开发语言·c++·经验分享
星星点点洲3 小时前
【Golang】数据设计模式
开发语言·设计模式·golang
步行cgn3 小时前
责任链设计模式详解
设计模式
今天也好累3 小时前
贪心算法之分数背包问题
c++·笔记·学习·算法·贪心算法
carver w3 小时前
c++,数据结构,unordermap哈希表基本操作
数据结构·c++·散列表