一、项目背景详细介绍

在大型软件系统中,我们经常会遇到这样一种设计困境:

  • 请求的发起者 与 请求的执行者 强耦合

  • 调用方需要了解接收方的具体实现细节

  • 随着功能增加,if-else / switch 急剧膨胀

  • 难以支持撤销(Undo)、重做(Redo)、宏命令等高级功能

例如在一个智能家居系统中:

按钮 -> 直接调用 灯.on()
按钮 -> 直接调用 空调.start()
按钮 -> 直接调用 音响.play()

这种写法的问题在于:

  • 按钮类依赖了所有设备类

  • 新增设备需要修改按钮代码

  • 完全违反 开闭原则

命令模式(Command Pattern) 正是为了解决这一问题而提出的。

命令模式属于 行为型设计模式,其核心思想是:

将“请求”封装成一个对象,从而使你可以用不同的请求对客户进行参数化,并支持撤销、排队和日志等操作。

本项目将使用 C++ 实现一个经典且教学友好的命令模式完整示例,模拟一个智能家居控制系统。


二、项目需求详细介绍

本项目的需求如下:

  1. 使用命令模式解耦“请求发送者”和“请求接收者”

  2. 支持多种具体命令(开灯、关灯、开空调)

  3. 支持命令的统一调用接口

  4. 支持命令撤销(Undo)操作

  5. 代码结构清晰,符合教学与博客发布需求

非功能性需求:

  • 符合命令模式标准 UML 结构

  • 代码可扩展性强

  • 适合作为设计模式课堂案例


三、相关技术详细介绍

3.1 命令模式核心角色

命令模式通常包含以下角色:

1. Command(抽象命令)

  • 定义执行命令的统一接口(如 execute()、undo())

2. ConcreteCommand(具体命令)

  • 绑定接收者对象

  • 实现命令的具体执行逻辑

3. Receiver(接收者)

  • 真正执行具体业务逻辑的对象

4. Invoker(调用者)

  • 触发命令执行

  • 不关心命令具体实现

5. Client(客户端)

  • 创建具体命令对象并设置其接收者


3.2 C++ 技术点

  • 抽象基类与多态

  • 智能指针(std::shared_ptr)

  • STL 容器(std::stack)


四、实现思路详细介绍

整体实现思路如下:

  1. 定义抽象命令接口 Command

  2. 所有具体命令继承该接口

  3. 每个具体命令内部组合一个接收者对象

  4. 调用者只负责调用 execute(),不关心细节

  5. 使用命令历史栈支持撤销操作

系统交互流程:

Client -> 创建命令
Invoker -> 执行命令
Command -> 调用 Receiver

五、完整实现代码

说明:所有代码放在一个代码块中,不同文件使用注释区分

/************************************************
 * 文件:Command.h
 ************************************************/
#include <iostream>
#include <memory>
#include <stack>

// 抽象命令
class Command {
public:
    virtual ~Command() {}
    virtual void execute() = 0;
    virtual void undo() = 0;
};

/************************************************
 * 文件:Receiver.h
 ************************************************/
class Light {
public:
    void on() {
        std::cout << "Light is ON" << std::endl;
    }
    void off() {
        std::cout << "Light is OFF" << std::endl;
    }
};

class AirConditioner {
public:
    void start() {
        std::cout << "AirConditioner is STARTED" << std::endl;
    }
    void stop() {
        std::cout << "AirConditioner is STOPPED" << std::endl;
    }
};

/************************************************
 * 文件:ConcreteCommand.h
 ************************************************/
class LightOnCommand : public Command {
private:
    std::shared_ptr<Light> light;
public:
    explicit LightOnCommand(std::shared_ptr<Light> l)
        : light(l) {}

    void execute() override {
        light->on();
    }

    void undo() override {
        light->off();
    }
};

class LightOffCommand : public Command {
private:
    std::shared_ptr<Light> light;
public:
    explicit LightOffCommand(std::shared_ptr<Light> l)
        : light(l) {}

    void execute() override {
        light->off();
    }

    void undo() override {
        light->on();
    }
};

class AirConditionerOnCommand : public Command {
private:
    std::shared_ptr<AirConditioner> ac;
public:
    explicit AirConditionerOnCommand(std::shared_ptr<AirConditioner> a)
        : ac(a) {}

    void execute() override {
        ac->start();
    }

    void undo() override {
        ac->stop();
    }
};

/************************************************
 * 文件:Invoker.h
 ************************************************/
class RemoteControl {
private:
    std::stack<std::shared_ptr<Command>> history;
public:
    void pressButton(std::shared_ptr<Command> command) {
        command->execute();
        history.push(command);
    }

    void pressUndo() {
        if (!history.empty()) {
            history.top()->undo();
            history.pop();
        }
    }
};

/************************************************
 * 文件:main.cpp
 ************************************************/
int main() {
    // 接收者
    auto light = std::make_shared<Light>();
    auto ac = std::make_shared<AirConditioner>();

    // 命令
    auto lightOn = std::make_shared<LightOnCommand>(light);
    auto lightOff = std::make_shared<LightOffCommand>(light);
    auto acOn = std::make_shared<AirConditionerOnCommand>(ac);

    // 调用者
    RemoteControl remote;

    remote.pressButton(lightOn);
    remote.pressButton(lightOff);
    remote.pressButton(acOn);

    // 撤销操作
    remote.pressUndo();
    remote.pressUndo();

    return 0;
}

六、代码详细解读(仅解读方法作用)

6.1 Command::execute

定义命令执行的统一接口。

6.2 Command::undo

定义撤销操作接口,用于回滚命令。

6.3 LightOnCommand::execute

调用接收者的开灯行为。

6.4 LightOnCommand::undo

撤销开灯操作,即关灯。

6.5 RemoteControl::pressButton

执行命令并保存历史记录。

6.6 RemoteControl::pressUndo

从历史记录中取出最近一次命令并撤销。


七、项目详细总结

通过该示例,我们完整实现了命令模式的标准结构,其优势包括:

  • 解耦请求发送者与接收者

  • 易于扩展新命令

  • 天然支持撤销、重做、宏命令

其缺点在于:

  • 类数量会随命令增加而增多


八、项目常见问题及解答

Q1:命令模式和策略模式的区别?

A:策略模式关注算法替换,命令模式关注请求封装。

Q2:撤销一定要用栈吗?

A:不是,但栈是最直观的实现方式。

Q3:是否支持宏命令?

A:支持,可将多个命令组合成一个命令。


九、扩展方向与性能优化

  1. 实现宏命令(MacroCommand)

  2. 增加命令队列与日志系统

  3. 支持重做(Redo)功能

  4. 与线程池结合实现异步命令

  5. 用函数对象(std::function)简化命令实现

更多推荐