设计模式-05 设计模式-命令行设计模式 Command Pattern
(1)定义
命令模式是一种设计模式,它将请求封装成一个对象,从而使您可以用不同的方式参数化请求、队列请求以及支持可撤销的操作。
命令模式将一个请求封装成一个对象,从而使您可以用不同的方式参数化请求、队列请求以及支持可撤销的操作。
将请求封装成对象:命令模式将每个请求封装成一个独立的对象,称为"命令"对象。
参数化请求:命令对象可以存储与请求相关的所有信息,包括接收者、动作和任何其他必要的参数。
队列请求:命令对象可以存储在一个队列中,以便按顺序或并发执行。
支持可撤销的操作:命令对象可以实现一个"撤销"方法,允许撤销先前执行的请求。
bash
+----------------+
| Command |
+----------------+
| - execute() |
+----------------+
+----------------+
| ConcreteCommand |
+----------------+
| - execute() |
+----------------+
+----------------+
| Invoker |
+----------------+
| - addCommand() |
| - invokeCommands() |
+----------------+
+----------------+
| Receiver |
+----------------+
| - action() |
+----------------+
调用关系:
Invoker 类拥有一个 Command 对象的集合。
当 Invoker 的 invokeCommands() 方法被调用时,它将依次调用集合中每个 Command 对象的 execute() 方法。
Command 对象的 execute() 方法负责调用 Receiver 对象的 action() 方法来执行实际操作。
bash
+----------------+
| Invoker |
+----------------+
|
v
+----------------+ +----------------+ +----------------+
| Command | | ConcreteCommand | | Command |
+----------------+ +----------------+ +----------------+
| |
v v
+----------------+ +----------------+
| Receiver | | Receiver |
+----------------+ +----------------+
图示中,Invoker 类调用 ConcreteCommand 对象的 execute() 方法,然后 ConcreteCommand 对象调用 Receiver 对象的 action() 方法来执行实际操作。
2.内涵
命令模式通常由以下类组成:
- Command:抽象命令接口,定义执行请求的方法。
- ConcreteCommand:实现 Command 接口的具体命令类,封装特定请求。
- Invoker:调用命令对象并管理它们的执行。
- Receiver:执行命令的实际对象。
3.使用示例
cpp
#include <iostream>
#include <vector>
// Command 接口
class Command {
public:
virtual void execute() = 0;
};
// ConcreteCommand 类
class ConcreteCommand : public Command {
private:
std::string _parameter;
public:
ConcreteCommand(const std::string& parameter) : _parameter(parameter) {}
void execute() override {
std::cout << "Executing command with parameter: " << _parameter << std::endl;
}
};
// Invoker 类
class Invoker {
private:
std::vector<Command> _commands;
public:
void addCommand(Command command) {
_commands.push_back(command);
}
void invokeCommands() {
for (auto command : _commands) {
command->execute();
}
}
};
int main() {
// 创建命令对象
Command command1 = new ConcreteCommand("Hello");
Command command2 = new ConcreteCommand("World");
// 创建调用者对象
Invoker invoker;
// 将命令添加到调用者
invoker.addCommand(command1);
invoker.addCommand(command2);
// 调用命令
invoker.invokeCommands();
// 清理
delete command1;
delete command2;
return 0;
}
4.注意事项
使用命令模式需要考虑注意事项:
- 命令对象可能变得复杂:如果命令需要执行复杂的操作或依赖于大量数据,则命令对象可能变得复杂且难以维护。
- 命令队列可能会变得庞大:如果需要执行大量命令,则命令队列可能会变得庞大且难以管理。
- 命令模式可能会引入额外的开销:由于每个请求都需要创建一个命令对象,因此命令模式可能会引入额外的开销。
- 撤销和重做操作可能很复杂:实现撤销和重做操作可能很复杂,尤其是在命令之间的依赖关系的情况下。
- 命令模式可能不适用于所有场景:命令模式最适合处理明确定义的请求,不适合处理需要动态或交互式行为的场景。
其他注意事项:
- 命令模式最适合于具有明确命令和接收者分离的场景。
- 如果命令之间存在复杂的关系,则命令模式可能不适合。
- 命令模式可能不适用于需要高性能的场景。
5.最佳实践
命令模式最佳实践:
- 使用命令封装请求:将每个请求封装在自己的命令对象中,以保持请求的独立性和可重用性。
- 将命令与接收者解耦:命令对象不应直接依赖于接收者对象。相反,命令应该通过接口或抽象基类与接收者交互。
- 使用命令队列:当需要按顺序或并行执行多个命令时,可以使用命令队列。
- 考虑撤销和重做操作:如果需要,可以实现撤销和重做操作,以允许用户撤销或重做先前执行的命令。
- 使用宏命令:宏命令可以将多个命令组合成一个单一的命令,从而简化复杂操作的执行。
- 保持命令对象轻量级:命令对象应尽可能保持轻量级,只包含执行请求所需的信息。
- 使用工厂方法创建命令:使用工厂方法可以简化命令创建过程,尤其是当有多种类型的命令时。
- 测试命令:编写单元测试以验证命令的正确性和健壮性。
6.总结
命令模式是一种有用的设计模式,可以提高代码的可测试性和可维护性,但重要的是要了解其注意事项并在适当的情况下使用它。