設計模式(十七)命令模式(行爲型)

概述

命令模式(Command Pattern):將一個請求封裝爲一個對象,從而讓咱們可用不一樣的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。命令模式是一種對象行爲型模式,其別名爲動做(Action)模式或事務(Transaction)模式。java

在命令模式結構圖中包含以下幾個角色:編程

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

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

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

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

命令模式的本質是對請求進行封裝,一個請求對應於一個命令,將發出命令的責任和執行命令的責任分割開。每個命令都是一個操做:請求的一方發出請求要求執行一個操做;接收的一方收到請求,並執行相應的操做。命令模式容許請求的一方和接收的一方獨立開來,使得請求的一方沒必要知道接收請求的一方的接口,更沒必要知道請求如何被接收、操做是否被執行、什麼時候被執行,以及是怎麼被執行的。設計

命令模式的關鍵在於引入了抽象命令類,請求發送者針對抽象命令類編程,只有實現了抽象命令類的具體命令才與請求接收者相關聯。在最簡單的抽象命令類中只包含了一個抽象的execute()方法,每一個具體命令類將一個Receiver類型的對象做爲一個實例變量進行存儲,從而具體指定一個請求的接收者,不一樣的具體命令類提供了execute()方法的不一樣實現,並調用不一樣接收者的請求處理方法。日誌

典型的抽象命令類代碼以下所示:code

abstract class Command {  
    public abstract void execute();  
}

對於請求發送者即調用者而言,將針對抽象命令類進行編程,能夠經過構造注入或者設值注入的方式在運行時傳入具體命令類對象並在業務方法中調用命令對象的execute()方法,其典型代碼以下所示:對象

class Invoker {  
    private Command command;  
      
    //構造注入  
    public Invoker(Command command) {  
        this.command = command;  
    }  
      
    //設值注入  
    public void setCommand(Command command) {  
        this.command = command;  
    }  
      
    //業務方法,用於調用命令類的execute()方法  
    public void call() {  
        command.execute();  
    }  
}

具體命令類繼承了抽象命令類,它與請求接收者相關聯,實現了在抽象命令類中聲明的execute()方法,並在實現時調用接收者的請求響應方法action(),其典型代碼以下所示:

class ConcreteCommand extends Command {  
    private Receiver receiver; //維持一個對請求接收者對象的引用  
  
    public void execute() {  
        receiver.action(); //調用請求接收者的業務處理方法action()  
    }  
}

請求接收者Receiver類具體實現對請求的業務處理,它提供了action()方法,用於執行與請求相關的操做,其典型代碼以下所示:

class Receiver {  
    public void action() {  
        //具體操做  
    }  
}

Demo

接收者角色,由錄音機類扮演

public class AudioPlayer {
    
    public void play(){
        System.out.println("播放...");
    }
    
    public void rewind(){
        System.out.println("倒帶...");
    }
    
    public void stop(){
        System.out.println("中止...");
    }
}

抽象命令角色類

public interface Command {
    /**
     * 執行方法
     */
    public void execute();
}

具體命令角色類

public class PlayCommand implements Command {

    private AudioPlayer myAudio;
    
    public PlayCommand(AudioPlayer audioPlayer){
        myAudio = audioPlayer;
    }
    /**
     * 執行方法
     */
    @Override
    public void execute() {
        myAudio.play();
    }
}

public class RewindCommand implements Command {

    private AudioPlayer myAudio;
    
    public RewindCommand(AudioPlayer audioPlayer){
        myAudio = audioPlayer;
    }
    @Override
    public void execute() {
        myAudio.rewind();
    }
}

public class StopCommand implements Command {
    private AudioPlayer myAudio;
    
    public StopCommand(AudioPlayer audioPlayer){
        myAudio = audioPlayer;
    }
    @Override
    public void execute() {
        myAudio.stop();
    }
}

請求者角色,由鍵盤類扮演

public class Keypad {
    private Command playCommand;
    private Command rewindCommand;
    private Command stopCommand;
    
    public void setPlayCommand(Command playCommand) {
        this.playCommand = playCommand;
    }
    public void setRewindCommand(Command rewindCommand) {
        this.rewindCommand = rewindCommand;
    }
    public void setStopCommand(Command stopCommand) {
        this.stopCommand = stopCommand;
    }
    /**
     * 執行播放方法
     */
    public void play(){
        playCommand.execute();
    }
    /**
     * 執行倒帶方法
     */
    public void rewind(){
        rewindCommand.execute();
    }
    /**
     * 執行播放方法
     */
    public void stop(){
        stopCommand.execute();
    }
}

客戶端角色,由茱麗小女孩扮演

public class Julia {
    public static void main(String[]args){
        //建立接收者對象
        AudioPlayer audioPlayer = new AudioPlayer();
        //建立命令對象
        Command playCommand = new PlayCommand(audioPlayer);
        Command rewindCommand = new RewindCommand(audioPlayer);
        Command stopCommand = new StopCommand(audioPlayer);
        //建立請求者對象
        Keypad keypad = new Keypad();
        keypad.setPlayCommand(playCommand);
        keypad.setRewindCommand(rewindCommand);
        keypad.setStopCommand(stopCommand);
        //測試
        keypad.play();
        keypad.rewind();
        keypad.stop();
        keypad.play();
        keypad.stop();
    }
}

播放...

倒帶...

中止...

播放...

中止...

宏命令

宏命令(Macro Command)又稱爲組合命令,它是組合模式和命令模式聯用的產物。宏命令是一個具體命令類,它擁有一個集合屬性,在該集合中包含了對其餘命令對象的引用。一般宏命令不直接與請求接收者交互,而是經過它的成員來調用接收者的方法。當調用宏命令的execute()方法時,將遞歸調用它所包含的每一個成員命令的execute()方法,一個宏命令的成員能夠是簡單命令,還能夠繼續是宏命令。執行一個宏命令將觸發多個具體命令的執行,從而實現對命令的批處理,其結構如圖所示:

宏命令Demo

系統須要一個表明宏命令的接口,以定義出具體宏命令所須要的接口。

public interface MacroCommand extends Command {
    /**
     * 宏命令彙集的管理方法
     * 能夠添加一個成員命令
     */
    public void add(Command cmd);
    /**
     * 宏命令彙集的管理方法
     * 能夠刪除一個成員命令
     */
    public void remove(Command cmd);
}

具體的宏命令MacroAudioCommand類負責把個別的命令合成宏命令。

public class MacroAudioCommand implements MacroCommand {
    
    private List<Command> commandList = new ArrayList<Command>();
    /**
     * 宏命令彙集管理方法
     */
    @Override
    public void add(Command cmd) {
        commandList.add(cmd);
    }
    /**
     * 宏命令彙集管理方法
     */
    @Override
    public void remove(Command cmd) {
        commandList.remove(cmd);
    }
    /**
     * 執行方法
     */
    @Override
    public void execute() {
        for(Command cmd : commandList){
            cmd.execute();
        }
    }
}

客戶端類Julia

public class Julia {
    
    public static void main(String[]args){
        //建立接收者對象
        AudioPlayer audioPlayer = new AudioPlayer();
        //建立命令對象
        Command playCommand = new PlayCommand(audioPlayer);
        Command rewindCommand = new RewindCommand(audioPlayer);
        Command stopCommand = new StopCommand(audioPlayer);
        
        MacroCommand marco = new MacroAudioCommand();
        
        marco.add(playCommand);
        marco.add(rewindCommand);
        marco.add(stopCommand);
        marco.execute();
    }
}

播放...

倒帶...

中止...

總結

命令模式是一種使用頻率很是高的設計模式,它能夠將請求發送者與接收者解耦,請求發送者經過命令對象來間接引用請求接收者,使得系統具備更好的靈活性和可擴展性。在基於GUI的軟件開發,不管是在電腦桌面應用仍是在移動應用中,命令模式都獲得了普遍的應用。

  • 主要優勢

命令模式的主要優勢以下:

  1. 下降系統的耦合度。因爲請求者與接收者之間不存在直接引用,所以請求者與接收者之間實現徹底解耦,相同的請求者能夠對應不一樣的接收者,一樣,相同的接收者也能夠供不一樣的請求者使用,二者之間具備良好的獨立性。
  2. 新的命令能夠很容易地加入到系統中。因爲增長新的具體命令類不會影響到其餘類,所以增長新的具體命令類很容易,無須修改原有系統源代碼,甚至客戶類代碼,知足「開閉原則」的要求。
  3. 能夠比較容易地設計一個命令隊列或宏命令(組合命令)
  4. 爲請求的撤銷(Undo)和恢復(Redo)操做提供了一種設計和實現方案

 

  • 主要缺點

命令模式的主要缺點以下:

使用命令模式可能會致使某些系統有過多的具體命令類。由於針對每個對請求接收者的調用操做都須要設計一個具體命令類,所以在某些系統中可能須要提供大量的具體命令類,這將影響命令模式的使用。

 

  • 適用場景

在如下狀況下能夠考慮使用命令模式:

  1. 系統須要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。請求調用者無須知道接收者的存在,也無須知道接收者是誰,接收者也無須關心什麼時候被調用。
  2. 系統須要在不一樣的時間指定請求、將請求排隊和執行請求。一個命令對象和請求的初始調用者能夠有不一樣的生命期,換言之,最初的請求發出者可能已經不在了,而命令對象自己仍然是活動的,能夠經過該命令對象去調用請求接收者,而無須關心請求調用者的存在性,能夠經過請求日誌文件等機制來具體實現。
  3. 系統須要支持命令的撤銷(Undo)操做和恢復(Redo)操做。
  4. 系統須要將一組操做組合在一塊兒造成宏命令。

我是天王蓋地虎的分割線

相關文章
相關標籤/搜索