C++命令模式
·
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,支持请求的排队、记录日志、撤销等操作。这种模式将请求的发送者和接收者解耦,使它们之间不直接交互。
命令模式的核心角色
- 命令(Command):定义执行操作的接口,通常包含一个
execute()方法 - 具体命令(Concrete Command):实现命令接口,绑定接收者和具体操作
- 接收者(Receiver):执行命令所对应的操作,知道如何实施与执行请求相关的操作
- 调用者(Invoker):要求命令执行请求,持有命令对象并安排其执行
- 客户端(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;
}
命令模式的工作原理
- 客户端创建具体命令对象,并将其与接收者关联
- 调用者(如遥控器)存储具体命令对象
- 当调用者执行命令时,它调用命令对象的
execute()方法 - 命令对象调用接收者的相应操作来完成请求
- 支持撤销操作时,命令对象还需实现
undo()方法,用于恢复到执行前的状态
命令模式的扩展应用
- 命令队列:将多个命令存储在队列中,依次执行
- 宏命令:将多个命令组合成一个复合命令,一次性执行
- 日志记录:记录命令的执行过程,支持系统崩溃后的恢复
- 事务管理:通过命令的执行与撤销实现事务的提交与回滚
命令模式的应用场景
- 需要实现撤销/重做功能的场景(如文本编辑器)
- 需要将请求排队执行的场景(如任务调度系统)
- 需要记录请求日志,以便后期回放的场景(如日志审计系统)
- 需要将请求发送者和接收者解耦的场景
- GUI中的菜单命令、按钮点击事件等
命令模式的优缺点
优点:
- 实现了请求发送者与接收者的解耦,降低了系统耦合度
- 支持命令的撤销与重做操作
- 便于扩展新命令,符合开放-封闭原则
- 可以组合多个命令形成复合命令(宏命令)
- 便于记录命令日志,支持事务管理
缺点:
- 可能导致系统中出现大量具体命令类,增加系统复杂度
- 命令的执行过程被封装,可能降低代码的可读性
命令模式在许多框架和系统中都有应用,例如GUI框架中的事件处理机制、数据库事务管理、命令行工具的命令处理等。在需要对操作进行封装、排队、记录或撤销的场景中,命令模式是一种非常有效的解决方案。
更多推荐



所有评论(0)