命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,支持请求的排队、记录日志、撤销等操作。这种模式将请求的发送者和接收者解耦,使它们之间不直接交互。

命令模式的核心角色

  1. 命令(Command):定义执行操作的接口,通常包含一个execute()方法
  2. 具体命令(Concrete Command):实现命令接口,绑定接收者和具体操作
  3. 接收者(Receiver):执行命令所对应的操作,知道如何实施与执行请求相关的操作
  4. 调用者(Invoker):要求命令执行请求,持有命令对象并安排其执行
  5. 客户端(Client):创建具体命令对象并设置其接收者

命令模式的实现示例

下面以"遥控器控制家电"为例展示命令模式的实现,遥控器(调用者)可以执行各种命令(如开灯、关灯、开电视等),并支持撤销操作:

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

// 前向声明
class Command;

// 接收者:家电设备
class Light {
private:
    std::string location;

public:
    Light(const std::string& loc) : location(loc) {}

    void on() {
        std::cout << location << " 灯已打开" << std::endl;
    }

    void off() {
        std::cout << location << " 灯已关闭" << std::endl;
    }
};

class TV {
private:
    std::string location;
    int channel;

public:
    TV(const std::string& loc) : location(loc), channel(0) {}

    void on() {
        std::cout << location << " 电视已打开" << std::endl;
    }

    void off() {
        std::cout << location << " 电视已关闭" << std::endl;
    }

    void setChannel(int ch) {
        channel = ch;
        std::cout << location << " 电视频道设置为 " << channel << std::endl;
    }
};

// 命令接口
class Command {
public:
    virtual void execute() = 0;
    virtual void undo() = 0;
    virtual ~Command() = default;
};

// 具体命令:开灯命令
class LightOnCommand : public Command {
private:
    Light* light;

public:
    LightOnCommand(Light* l) : light(l) {}

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

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

// 具体命令:关灯命令
class LightOffCommand : public Command {
private:
    Light* light;

public:
    LightOffCommand(Light* l) : light(l) {}

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

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

// 具体命令:开电视并设置频道命令
class TVOnCommand : public Command {
private:
    TV* tv;
    int previousChannel;

public:
    TVOnCommand(TV* t, int channel) : tv(t), previousChannel(0) {}

    void execute() override {
        previousChannel = 0;  // 记录当前频道(简化示例)
        tv->on();
        tv->setChannel(5);    // 默认打开5频道
    }

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

// 调用者:遥控器
class RemoteControl {
private:
    std::vector<std::unique_ptr<Command>> onCommands;
    std::vector<std::unique_ptr<Command>> offCommands;
    std::unique_ptr<Command> undoCommand;

public:
    RemoteControl(int slotCount) {
        // 初始化命令槽
        onCommands.resize(slotCount);
        offCommands.resize(slotCount);
        
        // 默认使用空命令
        class NoCommand : public Command {
        public:
            void execute() override {}
            void undo() override {}
        };
        
        std::unique_ptr<Command> noCommand = std::make_unique<NoCommand>();
        for (int i = 0; i < slotCount; ++i) {
            onCommands[i] = std::move(noCommand);
            offCommands[i] = std::make_unique<NoCommand>();
        }
        undoCommand = std::move(noCommand);
    }

    // 设置命令
    void setCommand(int slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) {
        onCommands[slot] = std::move(onCmd);
        offCommands[slot] = std::move(offCmd);
    }

    // 按下开按钮
    void pressOnButton(int slot) {
        onCommands[slot]->execute();
        undoCommand = std::move(onCommands[slot]);  // 保存最后执行的命令
    }

    // 按下关按钮
    void pressOffButton(int slot) {
        offCommands[slot]->execute();
        undoCommand = std::move(offCommands[slot]);  // 保存最后执行的命令
    }

    // 按下撤销按钮
    void pressUndoButton() {
        std::cout << "撤销操作: ";
        undoCommand->undo();
    }
};

// 客户端使用
int main() {
    // 创建遥控器(2个插槽)
    RemoteControl remote(2);

    // 创建接收者
    Light* livingRoomLight = new Light("客厅");
    TV* livingRoomTV = new TV("客厅");

    // 创建命令
    auto lightOn = std::make_unique<LightOnCommand>(livingRoomLight);
    auto lightOff = std::make_unique<LightOffCommand>(livingRoomLight);
    auto tvOn = std::make_unique<TVOnCommand>(livingRoomTV, 5);

    // 设置遥控器命令
    remote.setCommand(0, std::move(lightOn), std::move(lightOff));
    remote.setCommand(1, std::move(tvOn), std::make_unique<LightOffCommand>(livingRoomLight));  // 简化示例,用关灯命令作为电视关闭命令

    // 操作遥控器
    std::cout << "=== 按下客厅灯开按钮 ===" << std::endl;
    remote.pressOnButton(0);

    std::cout << "\n=== 按下客厅灯关按钮 ===" << std::endl;
    remote.pressOffButton(0);

    std::cout << "\n=== 按下撤销按钮 ===" << std::endl;
    remote.pressUndoButton();

    std::cout << "\n=== 按下客厅电视开按钮 ===" << std::endl;
    remote.pressOnButton(1);

    std::cout << "\n=== 按下撤销按钮 ===" << std::endl;
    remote.pressUndoButton();

    // 清理资源
    delete livingRoomTV;
    delete livingRoomLight;

    return 0;
}

命令模式的工作原理

  1. 客户端创建具体命令对象,并将其与接收者关联
  2. 调用者(如遥控器)存储具体命令对象
  3. 当调用者执行命令时,它调用命令对象的execute()方法
  4. 命令对象调用接收者的相应操作来完成请求
  5. 支持撤销操作时,命令对象还需实现undo()方法,用于恢复到执行前的状态

命令模式的扩展应用

  • 命令队列:将多个命令存储在队列中,依次执行
  • 宏命令:将多个命令组合成一个复合命令,一次性执行
  • 日志记录:记录命令的执行过程,支持系统崩溃后的恢复
  • 事务管理:通过命令的执行与撤销实现事务的提交与回滚

命令模式的应用场景

  1. 需要实现撤销/重做功能的场景(如文本编辑器)
  2. 需要将请求排队执行的场景(如任务调度系统)
  3. 需要记录请求日志,以便后期回放的场景(如日志审计系统)
  4. 需要将请求发送者和接收者解耦的场景
  5. GUI中的菜单命令、按钮点击事件等

命令模式的优缺点

优点

  • 实现了请求发送者与接收者的解耦,降低了系统耦合度
  • 支持命令的撤销与重做操作
  • 便于扩展新命令,符合开放-封闭原则
  • 可以组合多个命令形成复合命令(宏命令)
  • 便于记录命令日志,支持事务管理

缺点

  • 可能导致系统中出现大量具体命令类,增加系统复杂度
  • 命令的执行过程被封装,可能降低代码的可读性

命令模式在许多框架和系统中都有应用,例如GUI框架中的事件处理机制、数据库事务管理、命令行工具的命令处理等。在需要对操作进行封装、排队、记录或撤销的场景中,命令模式是一种非常有效的解决方案。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐