設計模式--命令模式Command(行為型)

benbenxiongyuan發表於2014-04-16

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)命令模式的關鍵在於引入了抽象命令介面,且傳送者針對抽象命令介面程式設計,只有實現了抽象命令介面的具體命令才能與接收者相關聯。 
 

相關文章