设计模式-05 设计模式-命令行设计模式 Command Pattern

发布于:2024-05-09 ⋅ 阅读:(20) ⋅ 点赞:(0)
设计模式-05 设计模式-命令行设计模式 Command Pattern 
 

(1)定义

命令模式是一种设计模式,它将请求封装成一个对象,从而使您可以用不同的方式参数化请求、队列请求以及支持可撤销的操作。

命令模式将一个请求封装成一个对象,从而使您可以用不同的方式参数化请求、队列请求以及支持可撤销的操作。
将请求封装成对象:命令模式将每个请求封装成一个独立的对象,称为“命令”对象。
参数化请求:命令对象可以存储与请求相关的所有信息,包括接收者、动作和任何其他必要的参数。
队列请求:命令对象可以存储在一个队列中,以便按顺序或并发执行。
支持可撤销的操作:命令对象可以实现一个“撤销”方法,允许撤销先前执行的请求。


+----------------+
| Command        |
+----------------+
| - execute()    |
+----------------+

+----------------+
| ConcreteCommand |
+----------------+
| - execute()    |
+----------------+

+----------------+
| Invoker        |
+----------------+
| - addCommand()  |
| - invokeCommands() |
+----------------+

+----------------+
| Receiver        |
+----------------+
| - action()      |
+----------------+

调用关系:

Invoker 类拥有一个 Command 对象的集合。
当 Invoker 的 invokeCommands() 方法被调用时,它将依次调用集合中每个 Command 对象的 execute() 方法。
Command 对象的 execute() 方法负责调用 Receiver 对象的 action() 方法来执行实际操作。

 

        +----------------+
          | Invoker        |
          +----------------+
               |
               v
+----------------+  +----------------+  +----------------+
| Command        |  | ConcreteCommand |  | Command        |
+----------------+  +----------------+  +----------------+
               |                              |
               v                              v
+----------------+                          +----------------+
| Receiver       |                         | Receiver       |
+----------------+                          +----------------+


        图示中,Invoker 类调用 ConcreteCommand 对象的 execute() 方法,然后 ConcreteCommand 对象调用 Receiver 对象的 action() 方法来执行实际操作。


2.内涵

命令模式通常由以下类组成:

  • Command:抽象命令接口,定义执行请求的方法。
  • ConcreteCommand:实现 Command 接口的具体命令类,封装特定请求。
  • Invoker:调用命令对象并管理它们的执行。
  • Receiver:执行命令的实际对象。

3.使用示例

#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.总结

命令模式是一种有用的设计模式,可以提高代码的可测试性和可维护性,但重要的是要了解其注意事项并在适当的情况下使用它。