設計模式 ( 十三 ) 命令模式Command(對象行為型)

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.結構

1.jpg

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圖:

2.jpg

宏命令又稱為組合命令,它是命令模式和組合模式聯用的產物:

宏命令也是一個具體命令,不過它包含了對其他命令對象的引用,在調用宏命令的execute()方法時,將遞歸調用它所包含的每個成員命令的execute()方法,一個宏命令的成員對象可以是簡單命令,還可以繼續是宏命令。執行一個宏命令將執行多個具體命令,從而實現對命令的批處理。

3.png

?

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

(0)
s19930811s19930811
上一篇 2015-07-15
下一篇 2015-07-16

相關推薦

  • 集群基本介紹

    Linux Cluster: Cluster:計算機集合,為解決某個特定問題組合起來形成的單個系統; Linux Cluster類型: LB:Load Balancing,負載均衡;主機來平均訪問的壓力。由負載均衡器和多個后端主機分擔主。 HA:High Availiablity,高可用,靠冗余節點實現;提高服務的可用性,有多個負載均衡器(和備份)來接收來自…

    Linux干貨 2016-10-30
  • LB Cluster 負載均衡集群 —-lvs 配置詳解

    lvs : ipvs scheduler: 根據其調度時是否考慮各RS當前的負載狀態,可分為靜態方法和動態方法兩種: 1、靜態方法:僅根據算法本身進行調度; RR:roundrobin,輪詢; WRR:Weighted RR,加權輪詢; SH:Source Hashing,實現session sticy,源IP地址hash;將來自于同一個IP地址的請求始終發…

    Linux干貨 2016-10-30
  • 查找幫助功能、Linux文件系統目錄標準LHS介紹

    查找幫助 命令自帶   COMMAND –help                      COMMAND -h 使用手冊 man COMMAND…

    Linux干貨 2016-10-30
  • Linux的終端類型

    1、串行端口終端(/dev/ttySn):       串行端口終端(Serial Port Terminal)是使用計算機串行端口連接的終端設備。計算機把每個串行端口都看作是一個字符設備。有段時間這些串行端口設備通常被稱為終端設備,因為那時它的最大用途就是用來連接終端。這些串行端口所對應的設備名稱是/dev…

    Linux干貨 2016-10-14
  • linux命令速記技巧

    linux命令速記技巧 linux命令有一定的命名規則,一個正常的coder在編碼的過程中都會考慮命名的語義化,開發者對函數的命名一定也盡量遵循這個規律.了解目錄所對應的英文全稱有助于自己的記憶,理解記憶獲得知識的留存率要遠大于死記硬背.如果英文基礎太差,小編建議先學好英文基本功再來學習這些命令. 命令的中文對照 命令 英文全稱 中文釋義 ls list 列…

    Linux干貨 2017-03-25
欧美性久久久久