设计模式之命令模式

1 概念定义

命令模式是一种行为型设计模式,它将一个请求封装为一个对象,从而允许用户使用不同的请求、队列或日志请求来参数化其他对象,并且支持可撤销的操作。

2 核心思想

  • 请求封装:将请求(操作)封装成对象

  • 参数化:可以用不同的请求对客户端进行参数化

  • 队列化:支持请求的排队、记录和撤销

  • 解耦:将请求的发送者和接收者解耦

3 主要角色

  • Command(抽象命令):声明执行操作的接口

  • ConcreteCommand(具体命令):实现Command接口,绑定接收者

  • Invoker(调用者):要求命令执行请求

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

  • Client(客户端):创建具体命令对象并设置其接收者

4 应用场景

以智能家居遥控系统为例:

有多种智能设备:灯光、空调、电视、窗帘等

每个设备有多个操作:开/关、调节温度、换台等

遥控器有多个可编程按钮

需要支持宏命令(一键执行多个操作)

需要支持撤销/重做功能

5 UML

6 C++代码实现

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <stack>
#include <map>
#include <functional>
using namespace std;

// ========== 前向声明 ==========
class Command;

// ========== 接收者:灯光 ==========
class Light {
private:
    string location;
    bool isOn;
    int brightness;  // 0-100
    string color;
    
public:
    Light(const string& loc) : location(loc), isOn(false), brightness(50), color("暖白") {}
    
    void turnOn() {
        isOn = true;
        cout << location << "灯光: 已开启" << endl;
    }
    
    void turnOff() {
        isOn = false;
        cout << location << "灯光: 已关闭" << endl;
    }
    
    void setBrightness(int level) {
        brightness = level;
        cout << location << "灯光: 亮度设置为 " << level << "%" << endl;
    }
    
    void setColor(const string& newColor) {
        color = newColor;
        cout << location << "灯光: 颜色设置为 " << newColor << endl;
    }
    
    bool getStatus() const { return isOn; }
    int getBrightness() const { return brightness; }
    string getColor() const { return color; }
};

// ========== 接收者:空调 ==========
class AirConditioner {
private:
    string location;
    bool isOn;
    int temperature;  // 16-30度
    int fanSpeed;     // 1-5档
    string mode;      // 制冷/制热/除湿
    
public:
    AirConditioner(const string& loc) : location(loc), isOn(false), 
                                        temperature(24), fanSpeed(3), mode("制冷") {}
    
    void turnOn() {
        isOn = true;
        cout << location << "空调: 已开启" << endl;
    }
    
    void turnOff() {
        isOn = false;
        cout << location << "空调: 已关闭" << endl;
    }
    
    void setTemperature(int temp) {
        temperature = temp;
        cout << location << "空调: 温度设置为 " << temp << "度" << endl;
    }
    
    void setFanSpeed(int speed) {
        fanSpeed = speed;
        cout << location << "空调: 风速设置为 " << speed << "档" << endl;
    }
    
    void setMode(const string& newMode) {
        mode = newMode;
        cout << location << "空调: 模式设置为 " << newMode << endl;
    }
    
    bool getStatus() const { return isOn; }
    int getTemperature() const { return temperature; }
    int getFanSpeed() const { return fanSpeed; }
    string getMode() const { return mode; }
};

// ========== 接收者:电视 ==========
class Television {
private:
    string location;
    bool isOn;
    int channel;
    int volume;
    string source;
    
public:
    Television(const string& loc) : location(loc), isOn(false), 
                                    channel(1), volume(20), source("TV") {}
    
    void turnOn() {
        isOn = true;
        cout << location << "电视: 已开启" << endl;
    }
    
    void turnOff() {
        isOn = false;
        cout << location << "电视: 已关闭" << endl;
    }
    
    void setChannel(int ch) {
        channel = ch;
        cout << location << "电视: 频道切换到 " << ch << endl;
    }
    
    void setVolume(int vol) {
        volume = vol;
        cout << location << "电视: 音量设置为 " << vol << endl;
    }
    
    void setSource(const string& src) {
        source = src;
        cout << location << "电视: 信号源切换到 " << src << endl;
    }
    
    bool getStatus() const { return isOn; }
    int getChannel() const { return channel; }
    int getVolume() const { return volume; }
    string getSource() const { return source; }
};

// ========== 接收者:窗帘 ==========
class Curtain {
private:
    string location;
    bool isOpen;
    int openness;  // 0-100%
    
public:
    Curtain(const string& loc) : location(loc), isOpen(true), openness(100) {}
    
    void open() {
        isOpen = true;
        openness = 100;
        cout << location << "窗帘: 已打开" << endl;
    }
    
    void close() {
        isOpen = false;
        openness = 0;
        cout << location << "窗帘: 已关闭" << endl;
    }
    
    void setOpenness(int percent) {
        openness = percent;
        isOpen = (percent > 0);
        cout << location << "窗帘: 开合度设置为 " << percent << "%" << endl;
    }
    
    bool getStatus() const { return isOpen; }
    int getOpenness() const { return openness; }
};

// ========== 抽象命令接口 ==========
class Command {
public:
    virtual ~Command() = default;
    
    // 执行命令
    virtual void execute() = 0;
    
    // 撤销命令
    virtual void undo() = 0;
    
    // 获取命令名称
    virtual string getName() const = 0;
};

// ========== 具体命令:开灯命令 ==========
class LightOnCommand : public Command {
private:
    Light* light;
    bool previousState;
    
public:
    LightOnCommand(Light* l) : light(l), previousState(false) {}
    
    void execute() override {
        previousState = light->getStatus();
        light->turnOn();
    }
    
    void undo() override {
        if (!previousState) {
            light->turnOff();
        } else {
            light->turnOn();
        }
    }
    
    string getName() const override {
        return "开灯";
    }
};

// ========== 具体命令:关灯命令 ==========
class LightOffCommand : public Command {
private:
    Light* light;
    bool previousState;
    
public:
    LightOffCommand(Light* l) : light(l), previousState(false) {}
    
    void execute() override {
        previousState = light->getStatus();
        light->turnOff();
    }
    
    void undo() override {
        if (previousState) {
            light->turnOn();
        } else {
            light->turnOff();
        }
    }
    
    string getName() const override {
        return "关灯";
    }
};

// ========== 具体命令:调节灯光亮度命令 ==========
class LightBrightnessCommand : public Command {
private:
    Light* light;
    int targetBrightness;
    int previousBrightness;
    
public:
    LightBrightnessCommand(Light* l, int brightness) 
        : light(l), targetBrightness(brightness), previousBrightness(0) {}
    
    void execute() override {
        previousBrightness = light->getBrightness();
        light->setBrightness(targetBrightness);
    }
    
    void undo() override {
        light->setBrightness(previousBrightness);
    }
    
    string getName() const override {
        return "调节亮度至" + to_string(targetBrightness) + "%";
    }
};

// ========== 具体命令:开空调命令 ==========
class AirConditionerOnCommand : public Command {
private:
    AirConditioner* ac;
    bool previousState;
    
public:
    AirConditionerOnCommand(AirConditioner* a) : ac(a), previousState(false) {}
    
    void execute() override {
        previousState = ac->getStatus();
        ac->turnOn();
    }
    
    void undo() override {
        if (!previousState) {
            ac->turnOff();
        }
    }
    
    string getName() const override {
        return "开空调";
    }
};

// ========== 具体命令:设置空调温度命令 ==========
class AirConditionerTempCommand : public Command {
private:
    AirConditioner* ac;
    int targetTemp;
    int previousTemp;
    
public:
    AirConditionerTempCommand(AirConditioner* a, int temp) 
        : ac(a), targetTemp(temp), previousTemp(0) {}
    
    void execute() override {
        previousTemp = ac->getTemperature();
        ac->setTemperature(targetTemp);
    }
    
    void undo() override {
        ac->setTemperature(previousTemp);
    }
    
    string getName() const override {
        return "设置温度至" + to_string(targetTemp) + "度";
    }
};

// ========== 具体命令:开电视命令 ==========
class TelevisionOnCommand : public Command {
private:
    Television* tv;
    bool previousState;
    
public:
    TelevisionOnCommand(Television* t) : tv(t), previousState(false) {}
    
    void execute() override {
        previousState = tv->getStatus();
        tv->turnOn();
    }
    
    void undo() override {
        if (!previousState) {
            tv->turnOff();
        }
    }
    
    string getName() const override {
        return "开电视";
    }
};

// ========== 具体命令:切换电视频道命令 ==========
class TelevisionChannelCommand : public Command {
private:
    Television* tv;
    int targetChannel;
    int previousChannel;
    
public:
    TelevisionChannelCommand(Television* t, int channel) 
        : tv(t), targetChannel(channel), previousChannel(0) {}
    
    void execute() override {
        previousChannel = tv->getChannel();
        tv->setChannel(targetChannel);
    }
    
    void undo() override {
        tv->setChannel(previousChannel);
    }
    
    string getName() const override {
        return "切换频道至" + to_string(targetChannel);
    }
};

// ========== 具体命令:开窗帘命令 ==========
class CurtainOpenCommand : public Command {
private:
    Curtain* curtain;
    bool previousState;
    
public:
    CurtainOpenCommand(Curtain* c) : curtain(c), previousState(false) {}
    
    void execute() override {
        previousState = curtain->getStatus();
        curtain->open();
    }
    
    void undo() override {
        if (!previousState) {
            curtain->close();
        }
    }
    
    string getName() const override {
        return "开窗帘";
    }
};

// ========== 宏命令:组合多个命令 ==========
class MacroCommand : public Command {
private:
    vector<Command*> commands;
    string name;
    
public:
    MacroCommand(const string& n) : name(n) {}
    
    void addCommand(Command* cmd) {
        commands.push_back(cmd);
    }
    
    void execute() override {
        cout << "\n执行宏命令: " << name << endl;
        for (auto cmd : commands) {
            cmd->execute();
        }
    }
    
    void undo() override {
        cout << "\n撤销宏命令: " << name << endl;
        // 逆序撤销
        for (auto it = commands.rbegin(); it != commands.rend(); ++it) {
            (*it)->undo();
        }
    }
    
    string getName() const override {
        return "宏:" + name;
    }
};

// ========== 空命令(用于初始化,避免空指针检查) ==========
class NoCommand : public Command {
public:
    void execute() override {
        // 什么都不做
    }
    
    void undo() override {
        // 什么都不做
    }
    
    string getName() const override {
        return "无命令";
    }
};

// ========== 调用者:智能遥控器 ==========
class SmartRemoteControl {
private:
    static const int SLOT_COUNT = 10;
    vector<Command*> onCommands;
    vector<Command*> offCommands;
    stack<Command*> history;  // 操作历史,用于撤销
    
public:
    SmartRemoteControl() {
        // 初始化所有插槽为空命令
        for (int i = 0; i < SLOT_COUNT; ++i) {
            onCommands.push_back(new NoCommand());
            offCommands.push_back(new NoCommand());
        }
    }
    
    ~SmartRemoteControl() {
        for (auto cmd : onCommands) delete cmd;
        for (auto cmd : offCommands) delete cmd;
    }
    
    // 设置命令到指定插槽
    void setCommand(int slot, Command* onCommand, Command* offCommand) {
        delete onCommands[slot];
        delete offCommands[slot];
        
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
    
    // 按下开按钮
    void onButtonPressed(int slot) {
        cout << "\n[按下开按钮 插槽" << slot << "]" << endl;
        Command* cmd = onCommands[slot];
        cmd->execute();
        history.push(cmd);
    }
    
    // 按无关按钮
    void offButtonPressed(int slot) {
        cout << "\n[按下关按钮 插槽" << slot << "]" << endl;
        Command* cmd = offCommands[slot];
        cmd->execute();
        history.push(cmd);
    }
    
    // 撤销上一次操作
    void undoButtonPressed() {
        cout << "\n[按下撤销按钮]" << endl;
        if (!history.empty()) {
            Command* lastCmd = history.top();
            lastCmd->undo();
            history.pop();
        } else {
            cout << "没有可撤销的操作" << endl;
        }
    }
    
    // 显示所有插槽的命令配置
    void showConfig() const {
        cout << "\n=== 遥控器配置 ===" << endl;
        for (int i = 0; i < SLOT_COUNT; ++i) {
            cout << "[插槽" << i << "] 开:" << onCommands[i]->getName() 
                 << " 关:" << offCommands[i]->getName() << endl;
        }
    }
};

// ========== 客户端:家庭自动化系统 ==========
class HomeAutomationSystem {
private:
    // 设备
    Light* livingRoomLight;
    Light* bedroomLight;
    AirConditioner* livingRoomAC;
    Television* livingRoomTV;
    Curtain* livingRoomCurtain;
    
    // 遥控器
    SmartRemoteControl* remote;
    
public:
    HomeAutomationSystem() {
        // 创建设备
        livingRoomLight = new Light("客厅");
        bedroomLight = new Light("卧室");
        livingRoomAC = new AirConditioner("客厅");
        livingRoomTV = new Television("客厅");
        livingRoomCurtain = new Curtain("客厅");
        
        // 创建遥控器
        remote = new SmartRemoteControl();
        
        // 配置遥控器
        setupRemoteControl();
    }
    
    ~HomeAutomationSystem() {
        delete livingRoomLight;
        delete bedroomLight;
        delete livingRoomAC;
        delete livingRoomTV;
        delete livingRoomCurtain;
        delete remote;
    }
    
    void setupRemoteControl() {
        // 插槽0:客厅灯
        remote->setCommand(0, 
            new LightOnCommand(livingRoomLight),
            new LightOffCommand(livingRoomLight));
        
        // 插槽1:卧室灯
        remote->setCommand(1,
            new LightOnCommand(bedroomLight),
            new LightOffCommand(bedroomLight));
        
        // 插槽2:空调
        remote->setCommand(2,
            new AirConditionerOnCommand(livingRoomAC),
            new AirConditionerTempCommand(livingRoomAC, 16));  // 关按钮设置为16度
        
        // 插槽3:电视
        remote->setCommand(3,
            new TelevisionOnCommand(livingRoomTV),
            new TelevisionChannelCommand(livingRoomTV, 1));  // 关按钮设置为频道1
        
        // 插槽4:窗帘
        remote->setCommand(4,
            new CurtainOpenCommand(livingRoomCurtain),
            new LightOnCommand(livingRoomLight));  // 演示:关按钮误设为开灯
        
        // 插槽5:调节客厅灯亮度
        remote->setCommand(5,
            new LightBrightnessCommand(livingRoomLight, 80),
            new LightBrightnessCommand(livingRoomLight, 20));
        
        // 插槽6:调节空调温度
        remote->setCommand(6,
            new AirConditionerTempCommand(livingRoomAC, 26),
            new AirConditionerTempCommand(livingRoomAC, 20));
        
        // 创建宏命令:观影模式
        MacroCommand* movieMode = new MacroCommand("观影模式");
        movieMode->addCommand(new LightBrightnessCommand(livingRoomLight, 20));
        movieMode->addCommand(new CurtainOpenCommand(livingRoomCurtain));
        movieMode->addCommand(new TelevisionOnCommand(livingRoomTV));
        movieMode->addCommand(new AirConditionerTempCommand(livingRoomAC, 25));
        
        // 插槽7:观影模式宏命令
        remote->setCommand(7, movieMode, new NoCommand());  // 没有对应的关命令
        
        // 插槽8:空
        remote->setCommand(8, new NoCommand(), new NoCommand());
        
        // 插槽9:全部关闭
        MacroCommand* allOff = new MacroCommand("全部关闭");
        allOff->addCommand(new LightOffCommand(livingRoomLight));
        allOff->addCommand(new LightOffCommand(bedroomLight));
        allOff->addCommand(new LightOnCommand(livingRoomAC));  // 错误演示:应该是关空调
        allOff->addCommand(new TelevisionOffCommand(livingRoomTV));
        allOff->addCommand(new CurtainCloseCommand(livingRoomCurtain));
        
        remote->setCommand(9, new NoCommand(), allOff);  // 关按钮执行全部关闭
    }
    
    void demonstrate() {
        cout << "=== 智能家居遥控系统演示 ===" << endl;
        
        // 显示遥控器配置
        remote->showConfig();
        
        // 测试基本命令
        cout << "\n【基本命令测试】" << endl;
        remote->onButtonPressed(0);   // 开客厅灯
        remote->onButtonPressed(1);   // 开卧室灯
        remote->offButtonPressed(0);  // 关客厅灯
        
        // 测试撤销
        cout << "\n【撤销测试】" << endl;
        remote->undoButtonPressed();  // 撤销关客厅灯
        remote->undoButtonPressed();  // 撤销开卧室灯
        
        // 测试参数化命令
        cout << "\n【参数化命令测试】" << endl;
        remote->onButtonPressed(5);   // 客厅灯亮度80%
        remote->onButtonPressed(6);   // 空调温度26度
        
        // 测试宏命令
        cout << "\n【宏命令测试】" << endl;
        remote->onButtonPressed(7);   // 观影模式
        
        // 测试全部关闭
        cout << "\n【全部关闭测试】" << endl;
        remote->offButtonPressed(9);  // 全部关闭
        
        // 多次撤销
        cout << "\n【多次撤销测试】" << endl;
        for (int i = 0; i < 3; ++i) {
            remote->undoButtonPressed();
        }
    }
};

// ========== 电视关命令(需要定义,上面未定义) ==========
class TelevisionOffCommand : public Command {
private:
    Television* tv;
    bool previousState;
    
public:
    TelevisionOffCommand(Television* t) : tv(t), previousState(false) {}
    
    void execute() override {
        previousState = tv->getStatus();
        tv->turnOff();
    }
    
    void undo() override {
        if (previousState) {
            tv->turnOn();
        }
    }
    
    string getName() const override {
        return "关电视";
    }
};

// ========== 窗帘关命令 ==========
class CurtainCloseCommand : public Command {
private:
    Curtain* curtain;
    bool previousState;
    
public:
    CurtainCloseCommand(Curtain* c) : curtain(c), previousState(false) {}
    
    void execute() override {
        previousState = curtain->getStatus();
        curtain->close();
    }
    
    void undo() override {
        if (previousState) {
            curtain->open();
        }
    }
    
    string getName() const override {
        return "关窗帘";
    }
};

// ========== 不使用命令模式的实现(对比) ==========
class BadRemoteControl {
private:
    Light* livingRoomLight;
    AirConditioner* ac;
    Television* tv;
    
    // 问题1:需要知道所有设备的具体接口
    // 问题2:按钮功能固定,无法动态改变
    // 问题3:无法实现撤销功能
    // 问题4:难以实现宏命令
    // 问题5:添加新设备需要修改类
    
public:
    BadRemoteControl() {
        livingRoomLight = new Light("客厅");
        ac = new AirConditioner("客厅");
        tv = new Television("客厅");
    }
    
    ~BadRemoteControl() {
        delete livingRoomLight;
        delete ac;
        delete tv;
    }
    
    // 按钮功能硬编码
    void button1Pressed() {
        livingRoomLight->turnOn();
    }
    
    void button2Pressed() {
        livingRoomLight->turnOff();
    }
    
    void button3Pressed() {
        ac->turnOn();
    }
    
    void button4Pressed() {
        ac->setTemperature(24);
    }
    
    void button5Pressed() {
        tv->turnOn();
    }
    
    void button6Pressed() {
        tv->setChannel(5);
    }
    
    // 问题:每个新功能都需要新按钮
    // 问题:无法动态重映射按钮
    // 问题:没有撤销功能
};

// ========== 主函数 ==========
int main() {
    HomeAutomationSystem home;
    home.demonstrate();
    
    cout << "\n=== 对比:不使用命令模式 ===" << endl;
    cout << "不使用命令模式的问题:" << endl;
    cout << "1. 请求发送者和接收者紧耦合" << endl;
    cout << "2. 无法参数化请求(如设置不同温度)" << endl;
    cout << "3. 无法实现撤销/重做功能" << endl;
    cout << "4. 难以实现宏命令" << endl;
    cout << "5. 添加新设备需要修改遥控器类" << endl;
    cout << "6. 无法记录操作日志" << endl;
    cout << "7. 无法支持事务性操作" << endl;
    
    return 0;
}

6 总结

不使用命令模式的坏处

如果不使用命令模式,通常会采用硬编码的方式实现遥控器:

方式1:硬编码按钮功能

cpp 复制代码
// 问题:按钮功能固定,无法动态改变
class FixedRemoteControl {
private:
    Light* light;
    AirConditioner* ac;
    
public:
    void button1Pressed() { light->turnOn(); }
    void button2Pressed() { light->turnOff(); }
    void button3Pressed() { ac->turnOn(); }
    void button4Pressed() { ac->setTemperature(24); }
    
    // 问题1:每个按钮功能固定
    // 问题2:无法重新配置按钮
    // 问题3:添加新设备需要添加新按钮
    // 问题4:无法实现撤销
    // 问题5:无法实现宏命令
    // 问题6:代码重复(每个设备都要类似的方法)
};

方式2:使用if-else判断

cpp 复制代码
// 问题:大量的条件判断
class IfElseRemote {
private:
    map<int, string> buttonMappings;  // 按钮到操作的映射
    
public:
    void buttonPressed(int buttonId) {
        string operation = buttonMappings[buttonId];
        
        if (operation == "light_on") {
            light->turnOn();
        }
        else if (operation == "light_off") {
            light->turnOff();
        }
        else if (operation == "ac_on") {
            ac->turnOn();
        }
        else if (operation == "ac_temp_24") {
            ac->setTemperature(24);
        }
        // 更多的else-if
        
        // 问题1:难以维护,每添加一个操作就要加一个else-if
        // 问题2:无法参数化(比如设置不同温度)
        // 问题3:无法实现撤销
        // 问题4:违反开闭原则
    }
};

方式3:使用函数指针(C风格)

cpp 复制代码
// 问题:类型不安全,难以管理状态
typedef void (*CommandFunction)();

class FunctionPointerRemote {
private:
    CommandFunction buttons[10];
    
public:
    void setButton(int index, CommandFunction func) {
        buttons[index] = func;
    }
    
    void buttonPressed(int index) {
        if (buttons[index]) {
            buttons[index]();
        }
    }
    
    // 问题1:无法处理带参数的函数
    // 问题2:无法保存状态(如之前的亮度)
    // 问题3:无法实现撤销
    // 问题4:无法组合命令
};

命令模式的优势

  • 解耦:请求发送者和接收者完全解耦

  • 可扩展性:添加新命令很容易,无需修改现有代码

  • 可组合性:可以组合简单命令形成复杂命令

  • 可撤销性:每个命令都知道如何撤销自己

  • 可队列化:命令可以被排队、调度、延迟执行

  • 可日志化:可以记录所有命令执行日志

  • 事务支持:可以支持事务(全部成功或全部回滚)

8 命令模式的使用场景

  • GUI按钮/菜单:每个按钮对应一个命令

  • 遥控器:可编程按钮

  • 多级撤销:编辑器撤销/重做

  • 事务处理:数据库事务

  • 任务队列:工作队列、线程池

  • 宏录制:录制和回放操作序列

  • 网络请求:请求封装为命令传输

  • 定时任务:延迟执行命令

命令模式通过将请求封装为对象,为处理请求提供了极大的灵活性和可扩展性。它不仅解耦了请求的发送者和接收者,还支持各种高级特性如撤销、重做、宏命令、事务处理等,是构建灵活、可扩展系统的重要工具。

相关推荐
五点六六六9 小时前
基于 AST 与 Proxy沙箱 的局部代码热验证
前端·设计模式·架构
wwdoffice01101 天前
304和316不锈钢有什么区别?哪个更好?
设计模式
网小鱼的学习笔记1 天前
创建型设计模式(工厂、builder、原型、单例)
java·后端·设计模式
逆境不可逃1 天前
【从零入门23种设计模式21】行为型之空对象模式
java·开发语言·数据库·算法·设计模式·职场和发展
蜜獾云2 天前
设计模式之命令模式:给其他模块下达命令
设计模式·命令模式
小湘西2 天前
拓扑排序(Topological Sort)
python·设计模式
蜜獾云2 天前
设计模式之观察者模式:监听目标对象的状态改变
观察者模式·设计模式·rxjava
知无不研2 天前
中介者模式
c++·设计模式·中介者模式
bmseven2 天前
大白话讲解23种设计模式简介
设计模式