1. 命令模式:解耦请求与执行的利器
在C++开发中,我们经常遇到这样的场景:某个对象需要执行请求,但又不确定具体由谁执行、何时执行。比如GUI中的按钮点击、游戏中的技能释放、事务管理中的操作回滚等。命令模式(Command Pattern)正是为解决这类问题而生。
我第一次真正理解命令模式的价值,是在开发一个图形编辑器时。当时需要实现撤销/重做功能,如果直接在按钮点击事件里调用具体绘图操作,代码会变得极其混乱。命令模式通过将"请求"封装成独立对象,完美解决了这个问题。它不仅让代码更清晰,还意外地带来了许多扩展可能性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 命令模式的核心结构解析
2.1 经典UML类图实现
标准的命令模式包含以下几个关键角色:
code复制Client -> Invoker
Invoker -> Command
Command <|-- ConcreteCommand
ConcreteCommand -> Receiver
在C++中,我们通常会这样实现:
cpp复制// 命令接口
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0; // 撤销操作
};
// 具体命令
class ConcreteCommand : public Command {
public:
ConcreteCommand(Receiver* receiver) : receiver_(receiver) {}
void execute() override {
receiver_->action();
}
void undo() override {
receiver_->reverseAction();
}
private:
Receiver* receiver_;
};
// 调用者
class Invoker {
public:
void setCommand(Command* cmd) {
command_ = cmd;
}
void executeCommand() {
command_->execute();
}
private:
Command* command_;
};
2.2 现代C++的改进实现
随着C++11/14/17的演进,我们可以用更现代的方式实现命令模式:
cpp复制#include <functional>
#include <memory>
using Command = std::function<void()>;
class ModernInvoker {
public:
void setCommand(Command cmd) {
command_ = std::move(cmd);
}
void executeCommand() {
if(command_) command_();
}
private:
Command command_;
};
这种实现利用了std::function和lambda表达式,代码更加简洁。但要注意,这种简化版可能不适合需要复杂撤销操作的场景。
3. 命令模式的五种典型应用场景
3.1 图形编辑器中的撤销/重做
这是命令模式最经典的应用。每个编辑操作(如添加形状、修改颜色)都被封装为一个命令对象:
cpp复制class AddShapeCommand : public Command {
public:
AddShapeCommand(Document& doc, ShapeType type)
: doc_(doc), type_(type) {}
void execute() override {
addedShape_ = doc_.addShape(type_);
}
void undo() override {
doc_.removeShape(addedShape_);
}
private:
Document& doc_;
ShapeType type_;
Shape* addedShape_ = nullptr;
};
通过维护命令历史栈,实现撤销/重做变得非常简单:
cpp复制std::stack<std::unique_ptr<Command>> history;
// 执行新命令
auto cmd = std::make_unique<AddShapeCommand>(doc, ShapeType::Circle);
cmd->execute();
history.push(std::move(cmd));
// 撤销
if(!
