C++实现设计模式---命令模式 (Command)

命令模式 (Command)

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


意图
  • 将操作的调用者与接收者分离,通过将请求封装为独立对象,使得请求更加灵活。
  • 支持撤销、重做、记录日志等操作。

使用场景
  1. 需要参数化请求
    • 客户端不直接调用操作,而是通过封装的命令对象。
  2. 需要支持撤销 (Undo) 或重做 (Redo)
    • 操作需要记录历史,以支持回滚或重试。
  3. 请求需要队列化
    • 系统需要对请求排队处理或记录日志。

参与者角色
  1. 命令接口 (Command)
    • 定义所有命令的公共接口。
  2. 具体命令类 (ConcreteCommand)
    • 实现命令接口,调用接收者执行具体操作。
  3. 接收者 (Receiver)
    • 负责执行具体操作的对象。
  4. 调用者 (Invoker)
    • 负责调用命令。
  5. 客户端 (Client)
    • 创建命令对象,并将其传递给调用者。

示例代码

以下代码展示了命令模式的实现,模拟智能家居系统控制灯光的打开、关闭操作,并支持撤销功能。

cpp 复制代码
#include <iostream>
#include <memory>
#include <stack>
#include <string>

// 命令接口:定义命令的公共接口
class Command {
public:
    virtual ~Command() = default;

    // 执行命令
    virtual void execute() = 0;

    // 撤销命令
    virtual void undo() = 0;
};

// 接收者:灯
class Light {
private:
    std::string name; // 灯的名称

public:
    explicit Light(std::string name) : name(std::move(name)) {}

    void turnOn() {
        std::cout << name << " 灯已打开。
";
    }

    void turnOff() {
        std::cout << name << " 灯已关闭。
";
    }
};

// 具体命令类:打开灯的命令
class LightOnCommand : public Command {
private:
    Light& light; // 具体接收者:灯

public:
    explicit LightOnCommand(Light& light) : light(light) {}

    void execute() override {
        light.turnOn(); // 打开灯
    }

    void undo() override {
        light.turnOff(); // 撤销,关闭灯
    }
};

// 具体命令类:关闭灯的命令
class LightOffCommand : public Command {
private:
    Light& light; // 具体接收者:灯

public:
    explicit LightOffCommand(Light& light) : light(light) {}

    void execute() override {
        light.turnOff(); // 关闭灯
    }

    void undo() override {
        light.turnOn(); // 撤销,打开灯
    }
};

// 调用者:遥控器
class RemoteControl {
private:
    std::stack<std::unique_ptr<Command>> commandHistory; // 存储命令历史

public:
    void executeCommand(std::unique_ptr<Command> command) {
        command->execute(); // 执行命令
        commandHistory.push(std::move(command)); // 将命令存入历史
    }

    void undoLastCommand() {
        if (!commandHistory.empty()) {
            auto& lastCommand = commandHistory.top(); // 获取最近的命令
            lastCommand->undo(); // 撤销命令
            commandHistory.pop(); // 移除该命令
        } else {
            std::cout << "无可撤销的命令。
";
        }
    }
};

// 客户端代码
int main() {
    Light livingRoomLight("客厅");
    Light bedroomLight("卧室");

    RemoteControl remoteControl;

    // 打开客厅灯
    remoteControl.executeCommand(std::make_unique<LightOnCommand>(livingRoomLight));
    // 关闭客厅灯
    remoteControl.executeCommand(std::make_unique<LightOffCommand>(livingRoomLight));
    // 打开卧室灯
    remoteControl.executeCommand(std::make_unique<LightOnCommand>(bedroomLight));
    // 撤销最近一次操作
    remoteControl.undoLastCommand();
    // 撤销最近一次操作
    remoteControl.undoLastCommand();

    return 0;
}

代码解析
1. 命令接口 (Command)
  • 定义了命令的公共接口,所有具体命令都需要实现 executeundo 方法。
cpp 复制代码
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};
2. 接收者 (Light)
  • 实现灯的具体操作,包括 turnOn(打开灯)和 turnOff(关闭灯)。
  • 是命令的实际执行者。
cpp 复制代码
class Light {
private:
    std::string name;
public:
    explicit Light(std::string name) : name(std::move(name)) {}
    void turnOn() { std::cout << name << " 灯已打开。
"; }
    void turnOff() { std::cout << name << " 灯已关闭。
"; }
};
3. 具体命令类
  • LightOnCommand
    • execute 方法中调用 turnOn 打开灯,在 undo 方法中调用 turnOff 撤销。
  • LightOffCommand
    • execute 方法中调用 turnOff 关闭灯,在 undo 方法中调用 turnOn 撤销。
cpp 复制代码
class LightOnCommand : public Command {
private:
    Light& light;
public:
    explicit LightOnCommand(Light& light) : light(light) {}
    void execute() override { light.turnOn(); }
    void undo() override { light.turnOff(); }
};
4. 调用者 (RemoteControl)
  • RemoteControl 负责调用命令对象的 execute 方法。
  • 使用栈 (std::stack) 存储命令历史,以支持撤销。
cpp 复制代码
class RemoteControl {
private:
    std::stack<std::unique_ptr<Command>> commandHistory;
public:
    void executeCommand(std::unique_ptr<Command> command) {
        command->execute();
        commandHistory.push(std::move(command));
    }
    void undoLastCommand() {
        if (!commandHistory.empty()) {
            commandHistory.top()->undo();
            commandHistory.pop();
        } else {
            std::cout << "无可撤销的命令。
";
        }
    }
};
5. 客户端
  • 客户端创建具体命令对象,并通过调用者 RemoteControl 执行命令。
  • 通过 undoLastCommand 撤销命令。

优缺点
优点
  1. 解耦调用者与接收者
    • 调用者无需知道接收者的具体实现。
  2. 支持撤销和重做
    • 通过记录命令历史,支持操作的撤销和重做。
  3. 命令队列化
    • 可以轻松实现请求的排队处理。
缺点
  1. 类数量增加
    • 每个操作都需要定义一个具体命令类。
  2. 存储开销
    • 需要存储命令历史以支持撤销和重做。

适用场景
  1. 参数化请求
    • 将请求封装为独立对象,客户端无需直接调用。
  2. 操作的撤销和重做
    • 系统需要支持操作回滚。
  3. 请求队列化
    • 系统需要对请求进行排队或记录日志。

总结

命令模式通过将请求封装为对象,实现了请求的参数化、撤销、排队处理等功能,是一种优雅的行为模式。适用于需要解耦调用者和接收者的场景,尤其在支持撤销或重做的系统中表现出色。

相关推荐
CaffeinePro15 小时前
四⼤极简架构原则KISS/DRY/YAGNI/LOD
设计模式·架构
cpp_250116 小时前
P10098 [ROIR 2023] 地铁建设 (Day 2)
数据结构·c++·算法·贪心·二分答案·洛谷题解
霸道流氓气质16 小时前
SpringBoot中设计模式组合使用示例-策略、模板、观察者、门面、工厂、单例。
spring boot·后端·设计模式
hehelm17 小时前
IO 多路复用 — Reactor
linux·服务器·网络·数据库·c++
灵晔君17 小时前
【C++】二叉搜索树
开发语言·c++
ttod_qzstudio18 小时前
【软考设计模式】观察者模式:一对多依赖的自动通知与订阅解耦精讲
观察者模式·设计模式
鱼子星_18 小时前
【C++】内存管理:内存分布,new/delete的使用及细节处理,new/delete的底层,定位new表达式
开发语言·c++·笔记
鱼子星_18 小时前
【C++】string(上):string的基本使用
c++·笔记·字符串
神仙别闹19 小时前
编写基于C++ Huffman 算法的无损压缩程序和解压程序
java·c++·算法
炸膛坦客19 小时前
单片机/C/C++八股:(二十三)#define 和 typedef 的区别
c语言·c++·单片机