1.概述
在軟件設計中,我們經常需要向某些對象發送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是哪個,我們只需在程序運行時指定具體的請求接收者即可,此時,可以使用命令模式來進行設計,使得請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用關系更加靈活。
例子1:電視機遙控器 : 遙控器是請求的發送者,電視機是請求的接收者,遙控器上有一些按鈕如開,關,換頻道等按鈕就是具體命令,不同的按鈕對應電視機的不同操作。
2.問題
在軟件系統中,“行為請求者”與“行為實現者”通常呈現一種“緊耦合”。但在某些場合,比如要對行為進行“記錄、撤銷/重做、事務”等處理,這種無法抵御變化的緊耦合是不合適的。在這種情況下,如何將“行為請求者”與“行為實現者”解耦?
3.解決方案
命令模式(Command Pattern):將一個請求封裝為一個對象,從而使我們可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日志,以及支持可撤銷的操作。命令模式又稱為動作(Action)模式或事務(Transaction)模式。(Command Pattern: Encapsulate a request asan object, thereby letting youparameterize clients withdifferent requests,queueor log requests,andsupportundoable operations. )
4.適用性
1)系統需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。
2)系統需要在不同的時間指定請求、將請求排隊和執行請求。
3)系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作。
4)系統需要將一組操作組合在一起,即支持宏命令。
5.結構
6.模式的組成
抽象命令類(Command): 聲明執行操作的接口。調用接收者相應的操作,以實現執行的方法Execute。
具體命令類(ConcreteCommand): 創建一個具體命令對象并設定它的接收者。通常會持有接收者,并調用接收者的功能來完成命令要執行的操作。
調用者(Invoker): 要求該命令執行這個請求。通常會持有命令對象,可以持有很多的命令對象。
接收者(Receiver): 知道如何實施與執行一個請求相關的操作。任何類都可能作為一個接收者,只要它能夠實現命令要求實現的相應功能。
客戶類(Client): 創建具體的命令對象,并且設置命令對象的接收者。真正使用命令的客戶端是從Invoker來觸發執行。
7.效果
Command模式優點:
1) 降低系統的耦合度:Command模式將調用操作的對象與知道如何實現該操作的對象解耦。
2) Command是頭等的對象。它們可像其他的對象一樣被操縱和擴展。
3) 組合命令:你可將多個命令裝配成一個組合命令,即可以比較容易地設計一個命令隊列和宏命令。一般說來,組合命令是Composite模式的一個實例。
4) 增加新的Command很容易,因為這無需改變已有的類。
5)可以方便地實現對請求的Undo和Redo。
命令模式的缺點:
使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。
8.實現
電視機遙控器 :
電視機是請求的接收者,
遙控器是請求的發送者,
遙控器上有一些按鈕,不同的按鈕對應電視機的不同操作。抽象命令角色由一個命令接口來扮演,
有三個具體的命令類實現了抽象命令接口,這三個具體命令類分別代表三種操作:打開電視機、關閉電視機和切換頻道。
顯然,電視機遙控器就是一個典型的命令模式應用實例。
<?php /** * 電視機遙控器 : 電視機是請求的接收者, 遙控器是請求的發送者, 遙控器上有一些按鈕,不同的按鈕對應電視機的不同操作。抽象命令角色由一個命令接口來扮演, 有三個具體的命令類實現了抽象命令接口,這三個具體命令類分別代表三種操作:打開電視機、關閉電視機和切換頻道。 顯然,電視機遙控器就是一個典型的命令模式應用實例。 */ /** * The Command abstraction( 命令接口,聲明執行的操作). * In this case the implementation must return a result, * sometimes it only has side effects. */ interface ICommand { /** * 執行命令對應的操作 * * @param unknown_type $name * @param unknown_type $args */ function execute(); } /** * ConcreteCommand具體的命令實現對象:打開命令 */ class ConcreteCommandOpen implements ICommand { /** * 持有相應的接收者對象 */ private $_receiverTV = null; // /** * 示意,命令對象可以有自己的狀態 */ private $_state; /** * 構造方法,傳入相應的接收者對象 * @param receiver 相應的接收者對象 */ public function __construct($receiver){ $this->_receiverTV = $receiver; } public function execute() { //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能 $this->_receiverTV->actionOpen(); } } /** * ConcreteCommand具體的命令實現對象:關閉 */ class ConcreteCommandClose implements ICommand { /** * 持有相應的接收者對象 */ private $_receiverTV = null; // /** * 示意,命令對象可以有自己的狀態 */ private $_state; /** * 構造方法,傳入相應的接收者對象 * @param receiver 相應的接收者對象 */ public function __construct($receiver){ $this->_receiverTV = $receiver; } public function execute() { //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能 $this->_receiverTV->actionClose(); } } /** * ConcreteCommand具體的命令實現對象:換頻道 */ class ConcreteCommandChange implements ICommand { /** * 持有相應的接收者對象 */ private $_receiverTV = null; // /** * 示意,命令對象可以有自己的狀態 */ private $_state; /** * 構造方法,傳入相應的接收者對象 * @param receiver 相應的接收者對象 */ public function __construct($receiver){ $this->_receiverTV = $receiver; } public function execute() { //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能 $this->_receiverTV->actionChange(); } } /** * 接收者對象 */ class ReceiverTV { /** * 真正執行命令相應的打開操作 */ public function actionOpen(){ echo 'actionOpen<br/>'; } /** * 真正執行命令相應的關閉操作 */ public function actionClose(){ echo 'actionClose<br/>'; } /** * 真正執行命令相應的換頻道操作 */ public function actionChange(){ echo 'actionChange<br/>'; } } /** * 調用者Invoker:遙控器 */ class InvokerControler { /** * 持有命令對象 */ private $_commands = null; //ICommand /** * 設置調用者持有的命令對象 * @param command 命令對象 */ public function addCommand($command) { $classname = get_class($command); $this->_commands[$classname] = $command; } /** * 示意方法,要求命令執行請求 */ public function runCommand($cmdName) { //調用命令對象的執行方法 $this->_commands[$cmdName]->execute(); } } class Client { /** * 示意,負責創建命令對象,并設定它的接收者 */ public static function main(){ //創建電視接收者 $receiver = new ReceiverTV(); //創建Invoker $invoker = new InvokerControler(); //創建命令對象,設定它的接收者 $commandOpen = new ConcreteCommandOpen($receiver); //把命令對象設置進調用遙控器 $invoker->addCommand($commandOpen); //執行打開命令 $invoker->runCommand(get_class($commandOpen)); } } Client::main(); ?>UML圖:
宏命令又稱為組合命令,它是命令模式和組合模式聯用的產物:
宏命令也是一個具體命令,不過它包含了對其他命令對象的引用,在調用宏命令的execute()方法時,將遞歸調用它所包含的每個成員命令的execute()方法,一個宏命令的成員對象可以是簡單命令,還可以繼續是宏命令。執行一個宏命令將執行多個具體命令,從而實現對命令的批處理。
?
9.與其他相關模式
1)Composite模式(可被用來實現宏命令。
2)備忘錄Memento模式可用來保持某個狀態,命令用這一狀態來取消它的效果。在被放入歷史表列前必須被拷貝的命令起到一種原型的作用。
10.總結與分析
1)命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開。
2)每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,并執行操作
3)命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎么被接收,以及操作是否被執行、何時被執 行,以及是怎么被執行的。
4)命令模式使請求本身成為一個對象,這個對象和其他對象一樣可以被存儲和傳遞。
5)命令模式的關鍵在于引入了抽象命令接口,且發送者針對抽象命令接口編程,只有實現了抽象命令接口的具體命令才能與接收者相關聯。
轉自:http://blog.csdn.net/hguisu/article/details/7549895
原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2917