设计模式:命令模式 (Command Pattern)
命令模式概述
命令模式是一种行为设计模式,它将请求或操作封装为一个对象,从而可以参数化客户端对象,进行请求排队、记录请求日志,以及支持可撤销的操作。
核心思想:
"把请求变成对象" - 将操作调用者与操作执行者解耦;
适用场景口诀:"操作要封装,撤销要支持,队列要管理,解耦是宗旨。"
模式结构
主要包含四个角色:
-
Command(命令接口):声明执行操作的接口
-
ConcreteCommand(具体命令):实现命令接口,绑定接收者与动作
-
Invoker(调用者):要求命令对象执行请求
-
Receiver(接收者):知道如何执行与请求相关的操作
C++实现示例
场景:智能家居遥控器
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// 接收者:家电设备
class Light {
public:
void on() { std::cout << "灯光打开" << std::endl; }
void off() { std::cout << "灯光关闭" << std::endl; }
};
class Fan {
public:
void start() { std::cout << "风扇启动" << std::endl; }
void stop() { std::cout << "风扇停止" << std::endl; }
};
命令接口
// 命令接口
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0; // 支持撤销操作
};
具体命令
// 具体命令:灯光命令
class LightOnCommand : public Command {
Light& light;
public:
explicit LightOnCommand(Light& l) : light(l) {}
void execute() override { light.on(); }
void undo() override { light.off(); }
};
class LightOffCommand : public Command {
Light& light;
public:
explicit LightOffCommand(Light& l) : light(l) {}
void execute() override { light.off(); }
void undo() override { light.on(); }
};
// 具体命令:风扇命令
class FanStartCommand : public Command {
Fan& fan;
public:
explicit FanStartCommand(Fan& f) : fan(f) {}
void execute() override { fan.start(); }
void undo() override { fan.stop(); }
};
class FanStopCommand : public Command {
Fan& fan;
public:
explicit FanStopCommand(Fan& f) : fan(f) {}
void execute() override { fan.stop(); }
void undo() override { fan.start(); }
};
调用者
// 调用者:遥控器
class RemoteControl {
std::vector<std::unique_ptr<Command>> onCommands;
std::vector<std::unique_ptr<Command>> offCommands;
std::unique_ptr<Command> lastCommand;
public:
void setCommand(int slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) {
if (onCommands.size() <= slot) {
onCommands.resize(slot + 1);
offCommands.resize(slot + 1);
}
onCommands[slot] = std::move(onCmd);
offCommands[slot] = std::move(offCmd);
}
void pressOnButton(int slot) {
if (slot < onCommands.size() && onCommands[slot]) {
onCommands[slot]->execute();
lastCommand = onCommands[slot]->clone(); // 假设Command有clone方法
}
}
void pressOffButton(int slot) {
if (slot < offCommands.size() && offCommands[slot]) {
offCommands[slot]->execute();
lastCommand = offCommands[slot]->clone();
}
}
void pressUndoButton() {
if (lastCommand) {
lastCommand->undo();
lastCommand.reset();
}
}
};
客户端
// 客户端代码
int main() {
// 创建接收者
Light livingRoomLight;
Fan ceilingFan;
// 创建命令
auto lightOn = std::make_unique<LightOnCommand>(livingRoomLight);
auto lightOff = std::make_unique<LightOffCommand>(livingRoomLight);
auto fanStart = std::make_unique<FanStartCommand>(ceilingFan);
auto fanStop = std::make_unique<FanStopCommand>(ceilingFan);
// 设置遥控器
RemoteControl remote;
remote.setCommand(0, std::move(lightOn), std::move(lightOff)); // 槽位0控制灯
remote.setCommand(1, std::move(fanStart), std::move(fanStop)); // 槽位1控制风扇
// 测试遥控器
remote.pressOnButton(0); // 开灯
remote.pressOnButton(1); // 开风扇
remote.pressUndoButton(); // 撤销上一个命令(关风扇)
remote.pressOffButton(0); // 关灯
return 0;
}
命令模式的高级应用
1 支持命令队列和日志
class CommandQueue {
std::vector<std::unique_ptr<Command>> queue;
public:
void addCommand(std::unique_ptr<Command> cmd) {
queue.push_back(std::move(cmd));
}
void processCommands() {
for (auto& cmd : queue) {
cmd->execute();
}
queue.clear();
}
void undoAll() {
for (auto it = queue.rbegin(); it != queue.rend(); ++it) {
(*it)->undo();
}
}
};
2 宏命令(组合命令)
class MacroCommand : public Command {
std::vector<std::unique_ptr<Command>> commands;
public:
void addCommand(std::unique_ptr<Command> cmd) {
commands.push_back(std::move(cmd));
}
void execute() override {
for (auto& cmd : commands) {
cmd->execute();
}
}
void undo() override {
for (auto it = commands.rbegin(); it != commands.rend(); ++it) {
(*it)->undo();
}
}
};
// 使用示例
auto partyMode = std::make_unique<MacroCommand>();
partyMode->addCommand(std::make_unique<LightOnCommand>(livingRoomLight));
partyMode->addCommand(std::make_unique<FanStartCommand>(ceilingFan));
partyMode->execute(); // 同时开灯和开风扇
命令模式的优势
-
解耦调用者与执行者:调用者不需要知道具体实现细节
-
支持撤销/重做:可以轻松实现操作历史记录
-
支持命令队列:可以实现延迟执行或任务调度
-
支持组合命令:可以构建复杂的命令宏
-
易于扩展:新增命令不影响现有代码
实际应用场景
-
GUI操作:按钮点击、菜单命令
-
事务系统:支持回滚的操作
-
游戏开发:角色技能、AI行为
-
批处理系统:任务队列处理
-
网络请求:请求封装与重试机制
-
智能家居:"一键离家"关闭所有设备
-
宏录制:把多个操作录制成一个组合命令
与其他模式的对比
-
策略模式:都涉及封装行为,但命令模式更关注请求的封装和生命周期管理
命令模式 策略模式 核心 封装"做什么+怎么做" 封装"怎么做" 重点 操作的生命周期管理 算法的可替换性 类比 外卖订单(包含完整操作信息) 交通工具(只关心移动方式) -
备忘录模式:常与命令模式结合实现撤销功能
-
责任链模式:命令模式可以用于构建责任链中的处理单元
总结
命令模式在C++中的关键点:
-
使用抽象基类定义命令接口
-
具体命令类绑定接收者与动作
-
调用者只与命令接口交互
-
智能指针管理命令对象生命周期
-
可以扩展支持撤销、队列、日志等功能
通俗总结
一句话版
命令模式就像餐厅点餐——你把想要的操作写成"订单",厨房按单做菜,可以随时加菜、取消或重做。
生活化比喻
想象你在玩电子游戏:
-
技能按键就是命令对象(比如"火球术"命令)
-
游戏角色是接收者(真正执行技能)
-
键盘是调用者(只负责触发命令)
-
技能冷却列表就是命令队列
-
撤销按钮就是命令的undo操作
三大核心特点
-
操作变对象
-
把"开灯"这个动作变成一个可拿在手里的"开灯券"
-
可以传递、存储、排队甚至撕毁(撤销)
-
-
解耦三明治
// 紧耦合写法(不好) 按钮.onClick = []{ 灯.打开(); }; // 命令模式写法 按钮.setCommand(开灯命令); // 按钮不知道具体操作谁 开灯命令.execute(); // 命令知道要操作灯
-
后悔药机制
-
每个命令自带"反操作"(undo)
-
就像Ctrl+Z可以无限回退
-
终极理解技巧
记住这个"快递包裹"比喻:
-
你(客户端)是寄件人
-
快递单(命令对象)写着:
-
收件人(接收者)
-
要做什么(execute方法)
-
退货说明(undo方法)
-
-
快递员(调用者)只管送包裹,不关心内容
本质:把操作请求打包成独立对象,让它能像实物一样被传递和管理
更多推荐



所有评论(0)