一、命令模式简介
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户端进行参数化,支持请求的排队和日志记录,以及支持可撤销的操作。命令模式是最常见的方式之一。例如前面几十个设计模式提供的设计方法中a.out本身就是一个命令(可执行程序),当我们去执行a.out可以产生不同的行为或状态。
命令模式的结构图
命令模式的主要组成部分
命令接口(Command Interface): 定义了一个抽象的执行方法(通常是
execute()
方法)。具体命令(Concrete Command): 实现了命令接口,定义了一个接收者对象并调用接收者的相关操作。
接收者(Receiver): 负责实际处理请求的对象,执行具体的操作。
调用者(Invoker): 持有一个命令对象,并在适当的时候调用命令对象的
execute()
方法。客户端(Client): 创建具体命令对象,并设置接收者,然后将命令对象传递给调用者。
命令模式的应用场景
1. UI操作
- 撤销/重做功能:在图形用户界面(GUI)中,命令模式可以用于实现撤销和重做功能。例如,每个用户操作(如文本编辑、格式化、图形绘制)都可以被封装成一个命令对象,当用户选择撤销时,系统可以执行之前的操作并保存当前状态以备重做。
- 按钮操作:在 GUI 中,按钮可以绑定不同的命令。例如,一个“保存”按钮可能绑定到“保存文件”命令,另一个“打印”按钮绑定到“打印文档”命令。
2. 任务调度
- 任务排队和日志记录:在任务调度系统中,命令模式允许将任务封装成命令对象,并将这些任务排队执行或记录。这样可以实现任务的调度、日志记录和撤销操作。
- 定时任务:在定时任务系统中,可以将不同的定时任务封装成命令对象,并在指定的时间执行这些任务。
3. 交易处理
- 银行交易:在金融系统中,银行交易(如存款、取款、转账)可以被封装成命令对象,方便对交易进行管理、日志记录和撤销。
4. 操作系统命令
- 操作系统的命令行工具:许多操作系统提供命令行工具来执行各种操作,这些工具内部也使用命令模式来处理不同的操作。例如,Unix/Linux 中的grep、sed、awk三剑客等命令。
5. 宏命令
- 复杂操作:在需要执行一系列操作时,可以将这些操作封装成一个宏命令对象。例如,自动化脚本可以使用命令模式来执行一系列操作步骤,如备份文件、清理日志和发送通知。
6. 游戏开发
- 游戏操作:在游戏开发中,玩家的操作(如移动角色、攻击敌人)可以被封装成命令对象,这样可以支持复杂的操作序列、操作记录和撤销。
7. 远程控制
- 设备控制:在远程控制系统中(如智能家居系统),可以通过命令模式封装对各种设备的控制操作。例如,打开/关闭灯、调节温度、播放音乐等操作都可以封装成不同的命令对象,并由遥控器发出。
8. 网络请求
- 异步请求:在网络编程中,命令模式可以用来封装网络请求,支持异步执行和回调。例如,将网络请求封装成命令对象,可以实现请求的排队、重试和取消操作
二、命令模式的设计方法
设计命令模式模拟遥控器开关灯的行为
command.cpp
#include <iostream>
#include <vector>
#include <memory>
#include <string>
// 命令接口
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
// 接收者
class Light {
public:
void turnOn() {
std::cout << "The light is on" << std::endl;
}
void turnOff() {
std::cout << "The light is off" << std::endl;
}
};
// 具体命令:开灯
class LightOnCommand : public Command {
public:
LightOnCommand(Light& light) : light_(light) {}
void execute() override {
light_.turnOn();
}
void undo() override {
light_.turnOff();
}
private:
Light& light_;
};
// 具体命令:关灯
class LightOffCommand : public Command {
public:
LightOffCommand(Light& light) : light_(light) {}
void execute() override {
light_.turnOff();
}
void undo() override {
light_.turnOn();
}
private:
Light& light_;
};
// 调用者
class RemoteControl {
public:
void setCommand(std::shared_ptr<Command> command) {
command_ = command;
}
void pressButton() {
if (command_) {
command_->execute();
commandHistory.push_back(command_);
history.push_back(getCommandName(command_) + " 被执行");
}
}
void pressUndo() {
if (!commandHistory.empty()) {
auto lastCommand = commandHistory.back();
lastCommand->undo();
commandHistory.pop_back();
history.push_back(getCommandName(lastCommand) + " 被撤销");
}
}
void printHistory() const {
for (const auto& entry : history) {
std::cout << entry << std::endl;
}
}
private:
std::shared_ptr<Command> command_;
std::vector<std::shared_ptr<Command>> commandHistory;
std::vector<std::string> history;
std::string getCommandName(const std::shared_ptr<Command>& command) const {
if (std::dynamic_pointer_cast<LightOnCommand>(command)) {
return "开灯";
} else if (std::dynamic_pointer_cast<LightOffCommand>(command)) {
return "关灯";
}
return "无效";
}
};
// 模拟客户端行为
void doWorking() {
Light livingRoomLight;
auto lightOn = std::make_shared<LightOnCommand>(livingRoomLight);
auto lightOff = std::make_shared<LightOffCommand>(livingRoomLight);
RemoteControl remote;
// 按下开灯键
remote.setCommand(lightOn);
remote.pressButton();
// 又按下开灯键
remote.setCommand(lightOn);
remote.pressButton();
// 又又按下开灯键
remote.setCommand(lightOn);
remote.pressButton();
// 关灯
remote.setCommand(lightOff);
remote.pressButton();
// 撤销最后一个命令
remote.pressUndo();
// 输出历史记录
remote.printHistory();
}
int main() {
//start working
doWorking();
return 0;
}
运行效果
三、总结
命令模式是一个十分实用的行为型设计模式,将复杂的内部实现(请求)全部封装,对外显示一个命令形态,对客户端来说十分友好。