命令模式C++
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,使你可以用不同的请求对客户进行参数化,还能支持请求的排队、记录日志及撤销操作。这种模式将发送者和接收者解耦,发送者无需知道接收者的具体实现。
命令模式的核心角色
- Command(命令接口):声明执行操作的接口(通常是
execute()
方法) - ConcreteCommand(具体命令):实现命令接口,绑定接收者和具体操作
- Receiver(接收者):执行命令所对应的操作,知道如何实施具体行为
- Invoker(调用者):要求命令执行请求,持有命令对象
- Client(客户端):创建具体命令对象并设置其接收者
C++实现示例
以下以"智能家居控制系统"为例实现命令模式,支持对灯光、电视等设备的操作,以及命令的撤销功能:
#include <iostream>
#include <string>
#include <vector>
#include <memory>// 前向声明接收者类
class Light;
class Television;// 命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;virtual std::string getName() const = 0;
};// 接收者1:灯光
class Light {
private:std::string location;bool isOn;public:Light(std::string loc) : location(std::move(loc)), isOn(false) {}void on() {isOn = true;std::cout << location << "灯光 打开" << std::endl;}void off() {isOn = false;std::cout << location << "灯光 关闭" << std::endl;}bool getState() const {return isOn;}
};// 接收者2:电视
class Television {
private:std::string location;bool isOn;int channel;int volume;public:Television(std::string loc) : location(std::move(loc)), isOn(false), channel(1), volume(5) {}void on() {isOn = true;std::cout << location << "电视 打开" << std::endl;}void off() {isOn = false;std::cout << location << "电视 关闭" << std::endl;}void setChannel(int ch) {channel = ch;std::cout << location << "电视 频道设置为: " << channel << std::endl;}void setVolume(int vol) {volume = vol;std::cout << location << "电视 音量设置为: " << volume << std::endl;}bool getState() const {return isOn;}int getChannel() const {return channel;}int getVolume() const {return volume;}
};// 具体命令1:开灯
class LightOnCommand : public Command {
private:Light* light;bool previousState; // 用于撤销操作public:LightOnCommand(Light* l) : light(l) {}void execute() override {previousState = light->getState();light->on();}void undo() override {if (previousState) {light->on();} else {light->off();}std::cout << "撤销 " << getName() << std::endl;}std::string getName() const override {return light->getState() ? "开灯" : "关灯";}
};// 具体命令2:关灯
class LightOffCommand : public Command {
private:Light* light;bool previousState;public:LightOffCommand(Light* l) : light(l) {}void execute() override {previousState = light->getState();light->off();}void undo() override {if (previousState) {light->on();} else {light->off();}std::cout << "撤销 " << getName() << std::endl;}std::string getName() const override {return "关灯";}
};// 具体命令3:开电视
class TvOnCommand : public Command {
private:Television* tv;bool previousState;int previousChannel;int previousVolume;public:TvOnCommand(Television* t) : tv(t) {}void execute() override {previousState = tv->getState();previousChannel = tv->getChannel();previousVolume = tv->getVolume();tv->on();tv->setChannel(5); // 默认打开5频道tv->setVolume(7); // 默认音量7}void undo() override {if (previousState) {tv->on();tv->setChannel(previousChannel);tv->setVolume(previousVolume);} else {tv->off();}std::cout << "撤销 " << getName() << std::endl;}std::string getName() const override {return "开电视";}
};// 具体命令4:关电视
class TvOffCommand : public Command {
private:Television* tv;bool previousState;public:TvOffCommand(Television* t) : tv(t) {}void execute() override {previousState = tv->getState();tv->off();}void undo() override {if (previousState) {tv->on();} else {tv->off();}std::cout << "撤销 " << getName() << std::endl;}std::string getName() const override {return "关电视";}
};// 调用者:遥控器
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);}// 设置命令void setCommand(int slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) {if (slot >= 0 && slot < onCommands.size()) {onCommands[slot] = std::move(onCmd);offCommands[slot] = std::move(offCmd);}}// 按下开按钮void pressOnButton(int slot) {if (slot >= 0 && slot < onCommands.size() && onCommands[slot]) {onCommands[slot]->execute();undoCommand = std::move(onCommands[slot]); // 保存命令用于撤销}}// 按下关按钮void pressOffButton(int slot) {if (slot >= 0 && slot < offCommands.size() && offCommands[slot]) {offCommands[slot]->execute();undoCommand = std::move(offCommands[slot]); // 保存命令用于撤销}}// 按下撤销按钮void pressUndoButton() {if (undoCommand) {undoCommand->undo();} else {std::cout << "没有可撤销的操作" << std::endl;}}
};// 客户端代码
int main() {// 创建接收者Light* livingRoomLight = new Light("客厅");Light* bedroomLight = new Light("卧室");Television* livingRoomTv = new Television("客厅");// 创建命令auto livingRoomLightOn = std::make_unique<LightOnCommand>(livingRoomLight);auto livingRoomLightOff = std::make_unique<LightOffCommand>(livingRoomLight);auto bedroomLightOn = std::make_unique<LightOnCommand>(bedroomLight);auto bedroomLightOff = std::make_unique<LightOffCommand>(bedroomLight);auto tvOn = std::make_unique<TvOnCommand>(livingRoomTv);auto tvOff = std::make_unique<TvOffCommand>(livingRoomTv);// 创建遥控器(调用者),有3个槽位RemoteControl remote(3);// 给遥控器设置命令remote.setCommand(0, std::move(livingRoomLightOn), std::move(livingRoomLightOff));remote.setCommand(1, std::move(bedroomLightOn), std::move(bedroomLightOff));remote.setCommand(2, std::move(tvOn), std::move(tvOff));// 测试操作std::cout << "=== 执行一系列操作 ===" << std::endl;remote.pressOnButton(0); // 开客厅灯remote.pressOnButton(2); // 开客厅电视remote.pressOffButton(0); // 关客厅灯remote.pressOnButton(1); // 开卧室灯// 测试撤销std::cout << "\n=== 测试撤销操作 ===" << std::endl;remote.pressUndoButton(); // 撤销"开卧室灯"remote.pressUndoButton(); // 撤销"关客厅灯"remote.pressUndoButton(); // 撤销"开客厅电视"// 清理资源delete livingRoomLight;delete bedroomLight;delete livingRoomTv;return 0;
}
代码解析
-
命令接口(
Command
):定义了execute()
(执行命令)和undo()
(撤销命令)方法,所有具体命令都需实现这两个方法。 -
接收者(
Light
、Television
):实现具体的业务逻辑(如开灯、关灯、开电视等),命令对象会调用这些方法。 -
具体命令:
- 每个命令类绑定一个接收者和对应的操作(如
LightOnCommand
绑定Light
的on()
方法) - 实现
execute()
方法调用接收者的相应操作,并保存执行前的状态用于undo()
- 每个命令类绑定一个接收者和对应的操作(如
-
调用者(
RemoteControl
):- 持有多个命令对象(开/关命令对),通过按钮触发命令执行
- 记录上一个执行的命令,支持
undo()
操作
-
工作流程:客户端创建命令并绑定接收者,调用者通过命令间接操作接收者,实现了发送者与接收者的解耦。
命令模式的优缺点
优点:
- 实现了发送者与接收者的解耦,发送者无需知道接收者的具体实现
- 容易扩展新命令,符合开放-封闭原则
- 支持命令的排队、日志记录和撤销操作
- 可以组合多个命令形成复合命令(宏命令)
缺点:
- 可能导致系统中出现大量具体命令类,增加系统复杂度
- 命令的撤销/重做功能实现复杂,需要保存历史状态
适用场景
- 当需要将请求发送者与接收者解耦时
- 当需要支持命令的撤销、重做操作时
- 当需要将多个命令组合成复合命令时
- 当需要实现请求的排队执行或日志记录时
常见应用:
- 图形界面中的菜单操作、按钮点击
- 事务处理(支持提交和回滚)
- 任务调度系统(命令排队执行)
- 遥控器、游戏手柄等设备的操作抽象
命令模式通过将操作封装为对象,为系统带来了更大的灵活性和可扩展性,特别适合需要支持撤销、日志、事务等功能的场景。