命令模式概述

      命令模式是一种行为设计模式,它将请求或操作封装为一个对象,从而可以参数化客户端对象,进行请求排队、记录请求日志,以及支持可撤销的操作。

核心思想:

      "把请求变成对象" - 将操作调用者与操作执行者解耦;

      适用场景口诀:"操作要封装,撤销要支持,队列要管理,解耦是宗旨。"

模式结构

主要包含四个角色:

  1. Command(命令接口):声明执行操作的接口

  2. ConcreteCommand(具体命令):实现命令接口,绑定接收者与动作

  3. Invoker(调用者):要求命令对象执行请求

  4. Receiver(接收者):知道如何执行与请求相关的操作

C++实现示例

场景:智能家居遥控器

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// 接收者:家电设备
class Light {
public:
    void on() { std::cout << "灯光打开" << std::endl; }
    void off() { std::cout << "灯光关闭" << std::endl; }
};

class Fan {
public:
    void start() { std::cout << "风扇启动" << std::endl; }
    void stop() { std::cout << "风扇停止" << std::endl; }
};

 命令接口

// 命令接口
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;  // 支持撤销操作
};

具体命令 

// 具体命令:灯光命令
class LightOnCommand : public Command {
    Light& light;
public:
    explicit LightOnCommand(Light& l) : light(l) {}
    void execute() override { light.on(); }
    void undo() override { light.off(); }
};

class LightOffCommand : public Command {
    Light& light;
public:
    explicit LightOffCommand(Light& l) : light(l) {}
    void execute() override { light.off(); }
    void undo() override { light.on(); }
};

// 具体命令:风扇命令
class FanStartCommand : public Command {
    Fan& fan;
public:
    explicit FanStartCommand(Fan& f) : fan(f) {}
    void execute() override { fan.start(); }
    void undo() override { fan.stop(); }
};

class FanStopCommand : public Command {
    Fan& fan;
public:
    explicit FanStopCommand(Fan& f) : fan(f) {}
    void execute() override { fan.stop(); }
    void undo() override { fan.start(); }
};

 调用者 

// 调用者:遥控器
class RemoteControl {
    std::vector<std::unique_ptr<Command>> onCommands;
    std::vector<std::unique_ptr<Command>> offCommands;
    std::unique_ptr<Command> lastCommand;
public:
    void setCommand(int slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) {
        if (onCommands.size() <= slot) {
            onCommands.resize(slot + 1);
            offCommands.resize(slot + 1);
        }
        onCommands[slot] = std::move(onCmd);
        offCommands[slot] = std::move(offCmd);
    }

    void pressOnButton(int slot) {
        if (slot < onCommands.size() && onCommands[slot]) {
            onCommands[slot]->execute();
            lastCommand = onCommands[slot]->clone(); // 假设Command有clone方法
        }
    }

    void pressOffButton(int slot) {
        if (slot < offCommands.size() && offCommands[slot]) {
            offCommands[slot]->execute();
            lastCommand = offCommands[slot]->clone();
        }
    }

    void pressUndoButton() {
        if (lastCommand) {
            lastCommand->undo();
            lastCommand.reset();
        }
    }
};

客户端

// 客户端代码
int main() {
    // 创建接收者
    Light livingRoomLight;
    Fan ceilingFan;

    // 创建命令
    auto lightOn = std::make_unique<LightOnCommand>(livingRoomLight);
    auto lightOff = std::make_unique<LightOffCommand>(livingRoomLight);
    auto fanStart = std::make_unique<FanStartCommand>(ceilingFan);
    auto fanStop = std::make_unique<FanStopCommand>(ceilingFan);

    // 设置遥控器
    RemoteControl remote;
    remote.setCommand(0, std::move(lightOn), std::move(lightOff)); // 槽位0控制灯
    remote.setCommand(1, std::move(fanStart), std::move(fanStop)); // 槽位1控制风扇

    // 测试遥控器
    remote.pressOnButton(0);  // 开灯
    remote.pressOnButton(1);  // 开风扇
    remote.pressUndoButton(); // 撤销上一个命令(关风扇)
    remote.pressOffButton(0); // 关灯

    return 0;
}

命令模式的高级应用

1 支持命令队列和日志

class CommandQueue {
    std::vector<std::unique_ptr<Command>> queue;
public:
    void addCommand(std::unique_ptr<Command> cmd) {
        queue.push_back(std::move(cmd));
    }
    
    void processCommands() {
        for (auto& cmd : queue) {
            cmd->execute();
        }
        queue.clear();
    }
    
    void undoAll() {
        for (auto it = queue.rbegin(); it != queue.rend(); ++it) {
            (*it)->undo();
        }
    }
};

2 宏命令(组合命令)

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 (auto& cmd : commands) {
            cmd->execute();
        }
    }
    
    void undo() override {
        for (auto it = commands.rbegin(); it != commands.rend(); ++it) {
            (*it)->undo();
        }
    }
};

// 使用示例
auto partyMode = std::make_unique<MacroCommand>();
partyMode->addCommand(std::make_unique<LightOnCommand>(livingRoomLight));
partyMode->addCommand(std::make_unique<FanStartCommand>(ceilingFan));
partyMode->execute(); // 同时开灯和开风扇

命令模式的优势

  1. 解耦调用者与执行者:调用者不需要知道具体实现细节

  2. 支持撤销/重做:可以轻松实现操作历史记录

  3. 支持命令队列:可以实现延迟执行或任务调度

  4. 支持组合命令:可以构建复杂的命令宏

  5. 易于扩展:新增命令不影响现有代码

实际应用场景

  1. GUI操作:按钮点击、菜单命令

  2. 事务系统:支持回滚的操作

  3. 游戏开发:角色技能、AI行为

  4. 批处理系统:任务队列处理

  5. 网络请求:请求封装与重试机制

  6. 智能家居:"一键离家"关闭所有设备

  7. 宏录制:把多个操作录制成一个组合命令

与其他模式的对比

  • 策略模式:都涉及封装行为,但命令模式更关注请求的封装和生命周期管理

    命令模式 策略模式
    核心 封装"做什么+怎么做" 封装"怎么做"
    重点 操作的生命周期管理 算法的可替换性
    类比 外卖订单(包含完整操作信息) 交通工具(只关心移动方式)
  • 备忘录模式:常与命令模式结合实现撤销功能

  • 责任链模式:命令模式可以用于构建责任链中的处理单元

总结

命令模式在C++中的关键点:

  1. 使用抽象基类定义命令接口

  2. 具体命令类绑定接收者与动作

  3. 调用者只与命令接口交互

  4. 智能指针管理命令对象生命周期

  5. 可以扩展支持撤销、队列、日志等功能

通俗总结

一句话版

命令模式就像餐厅点餐——你把想要的操作写成"订单",厨房按单做菜,可以随时加菜、取消或重做。

生活化比喻

想象你在玩电子游戏:

  • 技能按键就是命令对象(比如"火球术"命令)

  • 游戏角色是接收者(真正执行技能)

  • 键盘是调用者(只负责触发命令)

  • 技能冷却列表就是命令队列

  • 撤销按钮就是命令的undo操作

三大核心特点

  1. 操作变对象

    • 把"开灯"这个动作变成一个可拿在手里的"开灯券"

    • 可以传递、存储、排队甚至撕毁(撤销)

  2. 解耦三明治

    // 紧耦合写法(不好)
    按钮.onClick = []{ 灯.打开(); };
    
    // 命令模式写法
    按钮.setCommand(开灯命令); // 按钮不知道具体操作谁
    开灯命令.execute();       // 命令知道要操作灯
  1. 后悔药机制

    • 每个命令自带"反操作"(undo)

    • 就像Ctrl+Z可以无限回退

终极理解技巧

记住这个"快递包裹"比喻:

  • 你(客户端)是寄件人

  • 快递单(命令对象)写着:

    • 收件人(接收者)

    • 要做什么(execute方法)

    • 退货说明(undo方法)

  • 快递员(调用者)只管送包裹,不关心内容

本质:把操作请求打包成独立对象,让它能像实物一样被传递和管理

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐