命令模式 (Command)
命令模式 是一种行为型设计模式,它将请求封装为一个对象,从而使得可以用不同的请求对客户端进行参数化、对请求排队或记录日志,以及支持可撤销的操作。
意图
- 将操作的调用者与接收者分离,通过将请求封装为独立对象,使得请求更加灵活。
- 支持撤销、重做、记录日志等操作。
使用场景
- 需要参数化请求 :
- 客户端不直接调用操作,而是通过封装的命令对象。
- 需要支持撤销 (Undo) 或重做 (Redo) :
- 操作需要记录历史,以支持回滚或重试。
- 请求需要队列化 :
- 系统需要对请求排队处理或记录日志。
参与者角色
- 命令接口 (Command)
- 定义所有命令的公共接口。
- 具体命令类 (ConcreteCommand)
- 实现命令接口,调用接收者执行具体操作。
- 接收者 (Receiver)
- 负责执行具体操作的对象。
- 调用者 (Invoker)
- 负责调用命令。
- 客户端 (Client)
- 创建命令对象,并将其传递给调用者。
示例代码
以下代码展示了命令模式的实现,模拟智能家居系统控制灯光的打开、关闭操作,并支持撤销功能。
cpp
#include <iostream>
#include <memory>
#include <stack>
#include <string>
// 命令接口:定义命令的公共接口
class Command {
public:
virtual ~Command() = default;
// 执行命令
virtual void execute() = 0;
// 撤销命令
virtual void undo() = 0;
};
// 接收者:灯
class Light {
private:
std::string name; // 灯的名称
public:
explicit Light(std::string name) : name(std::move(name)) {}
void turnOn() {
std::cout << name << " 灯已打开。
";
}
void turnOff() {
std::cout << name << " 灯已关闭。
";
}
};
// 具体命令类:打开灯的命令
class LightOnCommand : public Command {
private:
Light& light; // 具体接收者:灯
public:
explicit LightOnCommand(Light& light) : light(light) {}
void execute() override {
light.turnOn(); // 打开灯
}
void undo() override {
light.turnOff(); // 撤销,关闭灯
}
};
// 具体命令类:关闭灯的命令
class LightOffCommand : public Command {
private:
Light& light; // 具体接收者:灯
public:
explicit LightOffCommand(Light& light) : light(light) {}
void execute() override {
light.turnOff(); // 关闭灯
}
void undo() override {
light.turnOn(); // 撤销,打开灯
}
};
// 调用者:遥控器
class RemoteControl {
private:
std::stack<std::unique_ptr<Command>> commandHistory; // 存储命令历史
public:
void executeCommand(std::unique_ptr<Command> command) {
command->execute(); // 执行命令
commandHistory.push(std::move(command)); // 将命令存入历史
}
void undoLastCommand() {
if (!commandHistory.empty()) {
auto& lastCommand = commandHistory.top(); // 获取最近的命令
lastCommand->undo(); // 撤销命令
commandHistory.pop(); // 移除该命令
} else {
std::cout << "无可撤销的命令。
";
}
}
};
// 客户端代码
int main() {
Light livingRoomLight("客厅");
Light bedroomLight("卧室");
RemoteControl remoteControl;
// 打开客厅灯
remoteControl.executeCommand(std::make_unique<LightOnCommand>(livingRoomLight));
// 关闭客厅灯
remoteControl.executeCommand(std::make_unique<LightOffCommand>(livingRoomLight));
// 打开卧室灯
remoteControl.executeCommand(std::make_unique<LightOnCommand>(bedroomLight));
// 撤销最近一次操作
remoteControl.undoLastCommand();
// 撤销最近一次操作
remoteControl.undoLastCommand();
return 0;
}
代码解析
1. 命令接口 (Command)
- 定义了命令的公共接口,所有具体命令都需要实现
execute
和undo
方法。
cpp
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
};
2. 接收者 (Light)
- 实现灯的具体操作,包括
turnOn
(打开灯)和turnOff
(关闭灯)。 - 是命令的实际执行者。
cpp
class Light {
private:
std::string name;
public:
explicit Light(std::string name) : name(std::move(name)) {}
void turnOn() { std::cout << name << " 灯已打开。
"; }
void turnOff() { std::cout << name << " 灯已关闭。
"; }
};
3. 具体命令类
LightOnCommand
:- 在
execute
方法中调用turnOn
打开灯,在undo
方法中调用turnOff
撤销。
- 在
LightOffCommand
:- 在
execute
方法中调用turnOff
关闭灯,在undo
方法中调用turnOn
撤销。
- 在
cpp
class LightOnCommand : public Command {
private:
Light& light;
public:
explicit LightOnCommand(Light& light) : light(light) {}
void execute() override { light.turnOn(); }
void undo() override { light.turnOff(); }
};
4. 调用者 (RemoteControl)
RemoteControl
负责调用命令对象的execute
方法。- 使用栈 (
std::stack
) 存储命令历史,以支持撤销。
cpp
class RemoteControl {
private:
std::stack<std::unique_ptr<Command>> commandHistory;
public:
void executeCommand(std::unique_ptr<Command> command) {
command->execute();
commandHistory.push(std::move(command));
}
void undoLastCommand() {
if (!commandHistory.empty()) {
commandHistory.top()->undo();
commandHistory.pop();
} else {
std::cout << "无可撤销的命令。
";
}
}
};
5. 客户端
- 客户端创建具体命令对象,并通过调用者
RemoteControl
执行命令。 - 通过
undoLastCommand
撤销命令。
优缺点
优点
- 解耦调用者与接收者 :
- 调用者无需知道接收者的具体实现。
- 支持撤销和重做 :
- 通过记录命令历史,支持操作的撤销和重做。
- 命令队列化 :
- 可以轻松实现请求的排队处理。
缺点
- 类数量增加 :
- 每个操作都需要定义一个具体命令类。
- 存储开销 :
- 需要存储命令历史以支持撤销和重做。
适用场景
- 参数化请求 :
- 将请求封装为独立对象,客户端无需直接调用。
- 操作的撤销和重做 :
- 系统需要支持操作回滚。
- 请求队列化 :
- 系统需要对请求进行排队或记录日志。
总结
命令模式通过将请求封装为对象,实现了请求的参数化、撤销、排队处理等功能,是一种优雅的行为模式。适用于需要解耦调用者和接收者的场景,尤其在支持撤销或重做的系统中表现出色。