命令模式(Command Pattern)是一種數據驅動的設計模式,它屬於行爲型模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。
模式動機
在軟件設計中,我們經常需要向某些對象發送請求,但是並不知道請求的接收者是誰,也不知道被請求的操作是哪個,我們只需在程序運行時指定具體的請求接收者即可,此時,可以使用命令模式來進行設計,使得請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用關係更加靈活。
命令模式可以對發送者和接收者完全解耦,發送者與接收者之間沒有直接引用關係,發送請求的對象只需要知道如何發送請求,而不必知道如何完成請求。這就是命令模式的模式動機。
模式分析
命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開。
每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,並執行操作。
命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。
命令模式使請求本身成爲一個對象,這個對象和其他對象一樣可以被存儲和傳遞。
命令模式的關鍵在於引入了抽象命令接口,且發送者針對抽象命令接口編程,只有實現了抽象命令接口的具體命令才能與接收者相關聯。
主要解決:
在軟件系統中,行爲請求者與行爲實現者通常是一種緊耦合的關係,但某些場合,比如需要對行爲進行記錄、撤銷或重做、事務等處理時,這種無法抵禦變化的緊耦合的設計就不太合適。
何時使用:
在某些場合,比如要對行爲進行"記錄、撤銷/重做、事務"等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將"行爲請求者"與"行爲實現者"解耦?將一組行爲抽象爲對象,可以實現二者之間的鬆耦合。
應用實例:
struts 1 中的 action 核心控制器 ActionServlet 只有一個,相當於 Invoker,而模型層的類會隨着不同的應用有不同的模型類,相當於具體的 Command。(待了解)
優點:
- 降低了系統耦合度。
- 新的命令可以很容易添加到系統中去。
- 允許接收請求的一方決定是否要否決請求。
- 能較容易地設計一個命令隊列。
- 可以容易地實現對請求的撤銷和恢復。
- 在需要的情況下,可以較容易地將命令記入日誌。
缺點
使用命令模式可能會導致某些系統有過多的具體命令類。
使用場景
認爲是命令的地方都可以使用命令模式,比如:
- GUI 中每一個按鈕都是一條命令。
- 模擬 CMD。
注意事項:系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作,也可以考慮使用命令模式,見命令模式的擴展。
模式結構
命令模式包含如下角色:
- Command: 抽象命令類
- ConcreteCommand: 具體命令類
- Invoker: 調用者
- Receiver: 接收者
- Client:客戶類
最近了解了一個工具,Graphviz:
使用它可以生成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
總結
學的有點懵。。