C++ 设计模式《订单的撤销和重做》
👨🎓 模式名称:命令模式(Command)
👦 故事背景:
小明的“万能校园生活平台”新增了一键下单功能,每个订单会自动派送给骑手去完成。
但是用户开始频繁出现这类操作:
- ❌ 下完订单又想撤销
- 🔁 撤销后又反悔,想恢复订单
- 🧾 打印操作日志或恢复崩溃数据
这下糟了!如果没有一个“命令历史”机制,撤销和重做就成了噩梦。
于是小明决定:
我要用【命令模式】来搞定撤销与重做系统!
✅ 使用命令模式的设计
🔧 1. 定义命令接口
class Command {
public:virtual void execute() = 0;virtual void undo() = 0;virtual ~Command() {}
};
2. 接收者类(骑手)
class Rider {
public:void deliver(const std::string& order) {std::cout << "🚴♂️ 派送订单:" << order << std::endl;}void cancel(const std::string& order) {std::cout << "❌ 撤销订单:" << order << std::endl;}
};
3. 具体命令类(一个订单 = 一个命令对象)
class DeliverCommand : public Command {
public:DeliverCommand(Rider* r, const std::string& o) : rider(r), order(o) {}void execute() override {rider->deliver(order);}void undo() override {rider->cancel(order);}private:Rider* rider;std::string order;
};
4. 调度器(Invoker):支持撤销和重做
#include <stack>class CommandManager {
public:void executeCommand(Command* cmd) {cmd->execute();undoStack.push(cmd);// 清空 redo 栈while (!redoStack.empty()) redoStack.pop();}void undo() {if (!undoStack.empty()) {Command* cmd = undoStack.top();undoStack.pop();cmd->undo();redoStack.push(cmd);}}void redo() {if (!redoStack.empty()) {Command* cmd = redoStack.top();redoStack.pop();cmd->execute();undoStack.push(cmd);}}private:std::stack<Command*> undoStack;std::stack<Command*> redoStack;
};
🚀 示例使用
int main() {Rider rider;CommandManager manager;Command* order101 = new DeliverCommand(&rider, "奶茶订单#101");Command* order102 = new DeliverCommand(&rider, "烧烤订单#102");manager.executeCommand(order101); // 派送奶茶manager.executeCommand(order102); // 派送烧烤std::cout << "🚫 用户撤销一个订单:" << std::endl;manager.undo(); // 撤销烧烤订单std::cout << "↩️ 用户反悔,重做一个订单:" << std::endl;manager.redo(); // 重做烧烤订单// 清理delete order101;delete order102;
}
❌ 不使用命令模式时(撤销逻辑耦合)
#include <vector>
#include <string>class Rider {
public:void deliver(const std::string& order) {std::cout << "🚴 派送:" << order << std::endl;history.push_back(order);}void cancelLast() {if (!history.empty()) {std::cout << "❌ 撤销:" << history.back() << std::endl;history.pop_back();}}private:std::vector<std::string> history;
};int main() {Rider rider;rider.deliver("奶茶订单#101");rider.deliver("烧烤订单#102");rider.cancelLast(); // 只能撤销最后一单
}
🧨 问题:
- 撤销逻辑和执行逻辑耦合在 Rider 里
- 无法“重做”
- 扩展操作记录、日志、存储等困难
✅ 总结:命令模式的优势
| 场景 | 命令模式 | 传统写法 |
|---|---|---|
| 撤销操作 | ✅ 支持 | ❌ 手动处理 |
| 重做操作 | ✅ 支持 | ❌ |
| 日志记录 | ✅ 可序列化命令对象 | ❌ 不可持久化 |
| 解耦调用者/执行者 | ✅ 调用者不关心执行细节 | ❌ 代码耦合 |
