【设计模式】命令模式(Command Pattern)分析及源码

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

模式动机

在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。

模式分析

命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。

每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。

命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。

命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

主要解决:

在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

何时使用:

在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

应用实例:

struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。(待了解)

优点:
  1. 降低了系统耦合度。
  2. 新的命令可以很容易添加到系统中去。
  3. 允许接收请求的一方决定是否要否决请求。
  4. 能较容易地设计一个命令队列。
  5. 可以容易地实现对请求的撤销和恢复。
  6. 在需要的情况下,可以较容易地将命令记入日志。
缺点

使用命令模式可能会导致某些系统有过多的具体命令类。

使用场景

认为是命令的地方都可以使用命令模式,比如:

  1. GUI 中每一个按钮都是一条命令。
  2. 模拟 CMD。

注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

模式结构

命令模式包含如下角色:

  • Command: 抽象命令类
  • ConcreteCommand: 具体命令类
  • Invoker: 调用者
  • Receiver: 接收者
  • Client:客户类

最近了解了一个工具,Graphviz:

【可视化】使用 Graphviz 绘画 UML 图

使用它可以生成UML图:

在这里插入图片描述
这个布局有点丑,下次再改改:

digraph {
    node [shape=Mrecord, fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]

    Client [label="{
    Client
    }"]
    
    Invoker [label="{
    Invoker
    |
    - command : Command\l
    |
    + call() : void\l
    }"]

    Command [label="{
    Command
    |
    + execute() : void\l
    }"]

    ConcreteCommand [label="{
    ConcreteCommand
    |
    - receiver : Receiver\l
    |
    + execute() : void\l
    }"]    

    Receiver [label="{
    Receiver
    |
    + action() : void\l
    }"]

    {
        Client -> { Invoker, Receiver } [arrowhead=vee, style=dashed]
        Invoker -> Command [dir=back, arrowtail=odiamond]
        ConcreteCommand -> Command [arrowtail=onormal, style=dashed]
        ConcreteCommand -> Receiver [arrowhead=vee]
    }
}
代码
#include <iostream>

namespace command_pattern {
class command {
public:
    virtual void execute() = 0;
};// class command 

class receiver {
public:
    void action() {
        std::cout << "receiver action" << std::endl;
    }
};// class receiver

class concrete_command : public command {
public:
    concrete_command(receiver *receiver_arg) {
        this->receiver_ = receiver_arg;
    }
    void execute() override {
        std::cout << "concrete_command execute " << std::endl;
        receiver_->action();
    }
private:
    receiver *receiver_;
};// class concrete_command

class invoker {
public:
    invoker(command *command_arg) {
        this->command_ = command_arg;
    }
    void call() {
        std::cout << "invoker call " << std::endl;
        command_->execute();
    }

private:
    command *command_;
}; // class invoker
} // namespace command_pattern

int main() {
    command_pattern::receiver _receiver;
    command_pattern::concrete_command _concrete_command(&_receiver);
    command_pattern::invoker _invoker(&_concrete_command);
    _invoker.call();
    return 0;
}

执行结果为:

invoker call 
concrete_command execute 
receiver action
总结

学的有点懵。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章