設計模式解密(11)- 命令模式

◆ 前言:命令模式內容比較多,這裏作了拆分

命令模式基礎篇 :http://www.cnblogs.com/JsonShare/p/7202133.htmlhtml

命令模式擴展篇 - 宏命令:http://www.cnblogs.com/JsonShare/p/7206395.htmlgit

命令模式擴展篇 - 撤銷命令:http://www.cnblogs.com/JsonShare/p/7206513.htmlgithub

命令模式擴展篇 - 命令隊列:http://www.cnblogs.com/JsonShare/p/7206607.html數據庫

命令模式擴展篇 - 請求日誌:http://www.cnblogs.com/JsonShare/p/7206665.htmlide

一、簡介

定義:將一個請求封裝爲一個對象,從而使你能夠用不一樣的請求對客戶進行參數化,對請求排隊和記錄請求日誌,以及支持可撤銷的操做;測試

主要解決:在軟件系統中,行爲請求者與行爲實現者一般是一種緊耦合的關係,但某些場合,好比須要對行爲進行記錄、撤銷或重作、事務等處理時,這種緊耦合的設計就不太合適;this

英文:Commandspa

類型:行爲型模式設計

二、類圖及組成

(引)類圖:日誌

組成:
  ● Command(抽象命令類):抽象命令類通常是一個抽象類或接口,在其中聲明瞭用於執行請求的execute()等方法,經過這些方法能夠調用請求接收者的相關操做。

  ● ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方法,它對應具體的接收者對象,將接收者對象的動做綁定其中。在實現execute()方法時,將調用接收者對象的相關操做(Action)。

  ● Invoker(調用者):調用者即請求發送者,它經過命令對象來執行請求。一個調用者並不須要在設計時肯定其接收者,所以它只與抽象命令類之間存在關聯關係。在程序運行時能夠將一個具體命令對象注入其中,再調用具體命令對象的execute()方法,從而實現間接調用請求接收者的相關操做。

  ● Receiver(接收者):接收者請求與執行相關的操做,它具體實現對請求的業務處理。

  ● Client(客戶端角色類):最終的客戶端調用類。

代碼結構:

抽象命令角色類
public interface Command {
    /**
     * 執行方法
     */
    void execute();
}

具體命令角色類
public class ConcreteCommand implements Command {
    //持有相應的接收者對象
    private Receiver receiver = null;
    /**
     * 構造方法
     */
    public ConcreteCommand(Receiver receiver){
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        //一般會轉調接收者對象的相應方法,讓接收者來真正執行功能
        receiver.action();
    }
}

接收者角色類
public class Receiver {
    /**
     * 真正執行命令相應的操做
     */
    public void action(){
        System.out.println("執行操做");
    }
}

請求者角色類
public class Invoker {
    /**
     * 持有命令對象
     */
    private Command command = null;
    /**
     * 構造方法
     */
    public Invoker(Command command){
        this.command = command;
    }
    /**
     * 行動方法
     */
    public void action(){
        command.execute();
    }
}

客戶端角色類
public class Client {
    public static void main(String[] args) {
        //建立接收者
        Receiver receiver = new Receiver();
        //建立命令對象,設定它的接收者
        Command command = new ConcreteCommand(receiver);
        //建立請求者,把命令對象設置進去
        Invoker invoker = new Invoker(command);
        //執行方法
        invoker.action();
    }
}

三、實例引入

下面以電視機的開、關命令爲例,進行講解:

package com.designpattern.Command;
/**
 * 抽象命令角色類
 * @author Json
*/
public interface Command {
     /**
     * 執行方法
     */
    void execute();
}
package com.designpattern.Command;
/**
 * 接收者角色類  -- 電視
 * @author Json
*/
public class TV {
    /**
     * 打開方法
     */
    public void on(){
        System.out.println("電視打開");
    }
    /**
     * 關閉方法
     */
    public void off(){
        System.out.println("電視關閉");
    }
}
package com.designpattern.Command;
/**
 * 具體命令角色類  -- 電視打開命令
 * @author Json
*/
public class TVOnCommand implements Command {
    private TV tv;
    public TVOnCommand(TV tv){
        this.tv = tv;
    }
    
    @Override
    public void execute() {
        tv.on();
    }
}
package com.designpattern.Command;
/**
 * 具體命令角色類  -- 電視關閉命令
 * @author Json
*/
public class TVOffCommand implements Command {
    private TV tv;
    public TVOffCommand(TV tv){
        this.tv = tv;
    }
    
    @Override
    public void execute() {
        tv.off();
    }
}
package com.designpattern.Command;
/**
 * 請求者角色類  -- 遙控器Invoker
 * @author Json
*/
public class RemoteControl {
    private Command onCommand,offCommand;
    
    public RemoteControl(Command _on,Command _off){
        this.onCommand = _on;
        this.offCommand = _off;
    }
    
    public void turnOn(){
        onCommand.execute();
    }
    
    public void turnOff(){
        offCommand.execute();
    }
}

 測試:

package com.designpattern.Command;
/**
 * 客戶端角色類
 * @author Json
*/
public class Client {
    public static void main(String[] args) {
        //建立接收者
        TV receiver = new TV();
        //建立命令對象,設定它的接收者
        Command on_command = new TVOnCommand(receiver);
        //建立命令對象,設定它的接收者
        Command off_command = new TVOffCommand(receiver);
        //命令控制對象Invoker,把命令對象經過構造方法設置進去 或者 增長set方法set進去是同樣的(setCommand方法未寫)
        RemoteControl invoker = new RemoteControl(on_command,off_command);
        //執行方法  -- 打開電視
        invoker.turnOn();
        //執行方法 -- 關閉電視
        invoker.turnOff();
    }
}

 結果:

電視打開
電視關閉

關於例子的補充說明:

雖然代碼看似挺多,但其實命令模式的結構仍是比較清晰的,總的來講命令模式的使用流程就是首先建立一個抽象命令,而後建立多個具體命令實現抽象命令接口,而後建立一個命令接受者角色,它包含各類的行爲的具體實現,而後再有一個命令調用者角色,提供給客戶端,用於接收客戶端的參數;

四、命令模式的擴展

 因爲篇幅太長,這裏單獨拆分出去了;

傳送門:

  命令模式擴展篇 - 宏命令:http://www.cnblogs.com/JsonShare/p/7206395.html

  命令模式擴展篇 - 撤銷命令:http://www.cnblogs.com/JsonShare/p/7206513.html

  命令模式擴展篇 - 命令隊列:http://www.cnblogs.com/JsonShare/p/7206607.html

  命令模式擴展篇 - 請求日誌:http://www.cnblogs.com/JsonShare/p/7206665.html

五、優缺點

優勢:

  一、命令模式將行爲調用者和各類行爲分隔開,下降程序的耦合,便於程序擴展;

  二、命令模式將行爲的具體實現封裝起來,客戶端無需關心行爲的具體實現;

  三、命令模式可爲多種行爲提供統一的調用入口,便於程序對行爲的管理和控制;

缺點:

  使用命令模式的話,不用管命令多簡單,都須要寫一個命令類來封裝,使用命令模式可能會致使系統有過多的具體命令類;

     (上面的例子就能夠看出,每一個命令都要有一個具體的命令類);

六、應用場景

  一、但願將行爲請求者和行爲實現者解耦,不直接打交道;

  二、但願分離掉行爲請求者一部分的責任,行爲請求者只須要將命令發給調用者,再也不主動的去讓行爲實現者產生行爲,符合單一職責原則;

  三、但願能夠控制執行的命令列表,方便記錄,撤銷/重作以及事務等功能;

  四、期待能夠將請求排隊,有序執行;

  五、但願能夠將請求組合使用,即支持宏命令;

七、總結

  命令模式最大的好處就是實現了行爲請求者與行爲實現者的解耦;

  在實際場景中的使用:

      Struts2中action中的調用過程當中存在命令模式;

      數據庫中的事務機制的底層實現;

      命令的撤銷和恢復:增長相應的撤銷和恢復命令的方法(好比數據庫中的事務回滾);

  例如:Java的Runnable就是命令模式的變形應用:

public class Test {
    public static void main(String[] args) {
        //範例
        Runnable runnable = () -> System.out.println("具體命令"); // Command cmd = ConcreteCommand
        Thread thread1 = new Thread(runnable); // 將 cmd 交給 Thread (Invoker)
        thread1.start(); // Invoker 調用 cmd 的執行方法
    }
} 

 

 

PS:源碼地址   https://github.com/JsonShare/DesignPattern/tree/master 

   

PS:原文地址 http://www.cnblogs.com/JsonShare/p/7202133.html 

相關文章
相關標籤/搜索