設計模式 ( 十三 ) 命令模式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 22:28
下一篇 2015-07-16 22:26

相關推薦

  • 正則表達式以及Linux文本搜索工具grep

    一正則表達式 regual Expression  簡稱REGEXP 定義:由一類特殊字符以及文本字所編寫的模式,其中有些字符不表示其字面意義,而用于統配和統配功能 分類:     基本正則表達式:BRE 貪婪模式(盡可能長地去匹配符合模式的內容     擴展表達式:E…

    Linux干貨 2016-08-07
  • 密碼保護:第二天

    無法提供摘要。這是一篇受保護的文章。

    Linux干貨 2017-07-15
  • linux添加用戶

    今天給大家說一下linux添加用戶,大家可能覺得添加用戶很簡單,’adduser 用戶名’就這個命令搞掂了。那么大家知道這個命令幫我們完成了那些事情呢,今天就給大家普及一下 添加一個linux用戶需要關系到以下幾個文件: 保存用戶組的/etc/group文件。 保存用戶ID和密碼的/etc/passwd文件。 在home目錄下生產一個…

    Linux干貨 2017-05-28
  • 優云實踐:巧用Salt,實現CMDB配置自動發現

    隨著互聯網+新形勢的發展,越來越多的企業步入雙態(穩敏雙態)IT時代,信息化環境越來越復雜,既有IOE三層架構,也有VCE、Openstack等云虛擬化架構和互聯網化的分布式大數據架構。所以,企業急需建立一套合適的配置管理庫(CMDB),像人類“大腦”一樣統一存儲從基礎架構到業務應用各層面的配置信息,以便協調“身體”(運維系統)各部分完成復雜的運維工作。 C…

    系統運維 2016-07-26
  • N25-第二周博客作業

    1.linux上的文件管理命令:   cp命令:copy                 源文件;目標文件;  單源復制:cp [option]… [-T]  SOURCE DEST       &…

    Linux干貨 2016-12-12
  • bash腳本編程

    Linux腳本編程中bash常用的測試類型:                 整數測試:       -gt greater than  大于    …

    Linux干貨 2016-08-18
欧美性久久久久