【設計模式】命令模式(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
總結

學的有點懵。。

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