軟件設計模式學習(十八)命令模式


命令模式將請求發送者與請求接收者解耦,在發送者與接收者之間引入命令對象,將發送者的請求封裝在命令對象中,請求發送者經過命令對象來間接引用接收者,使得系統具備更好的靈活性,用戶能夠根據須要爲請求發送者增長新的命令對象而無須修改原有系統html


模式動機

舉個現實生活中的例子,開關是請求的發送者,電燈是請求的接收者,它們之間不存在直接的耦合關係,而是經過電線鏈接到一塊兒,開關不須要知道如何將開燈或關燈請求傳輸給電燈,而是經過電線來完成這項功能。java

此時能夠理解爲電線充當封裝請求的命令對象,開關若是開則電線通電,並調用電燈的開燈方法,反之則關燈。不一樣電線能夠鏈接不一樣的請求接收者,所以只需更換一根電線,相同的開關便可操做不一樣的電器設備。編程

在軟件設計中,咱們也像上述例子同樣,常常須要向某些對象發送請求,可是不知道請求接收者是誰,也不知道被請求的操做是哪一個,咱們只需指定具體的請求接收者便可,此時,可使用命令模式使請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用更加啊靈活。設計模式


模式定義

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


模式結構

  1. Command(抽象命令類)學習

    通常是接口,其中聲明瞭用於執行請求的 execute() 等方法,經過這些方法調用請求接收者的相關操做測試

  2. ConcreteCommand(具體命令類)this

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

  3. Invoker(調用者)3d

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

  4. Receiver(接收者)

    執行者執行與請求相關的操做,它具體實現對請求的業務處理

  5. Client(客戶類)

    客戶類中需建立調用者對象和具體命令對象,再建立具體命令對象時指定其對應接收者,發送者和接收者之間無直接關係,透過具體命令對象實現間接調用


模式分析

命令模式的本質其實就是將命令(Command)、發出命令的責任(Invoker)和執行命令的責任(Recevier)分隔開。請求的一方發出請求,要求執行一個操做,接收的一方收到請求,並執行操做,請求的一方沒必要知道接收請求一方的任何細節。

命令模式的關鍵在於引入抽象命令接口,調用者針(Invoker)對抽象命令接口編程,只有實現具體命令類才能與對應接收者相關聯。每一個具體命令類把接收者(Recevier)做爲一個實例變量存儲,從而指定接收者,並調用對應的請求處理方法

能夠經過順序圖來進一步理解命令模式中對象之間的相互關係。


模式實例之電視機遙控器

電視機是請求接收者,遙控器是請求發送者,遙控器上有一些不一樣按鈕,對應電視機的不一樣操做,分別是:打開電視機、關閉電視機和切換頻道。

  1. 接收者類 Television(電視機類)

    public class Televison {
    
        public void open() {
            System.out.println("打開電視機");
        }
    
        public void close() {
            System.out.println("關閉電視機");
        }
    
        public void changeChannel() {
            System.out.println("切換電視頻道");
        }
    }
  2. 抽象命令類 AbstractCommand(命令類)

    public interface AbstractCommand {
    
        public void execute();
    }
  3. 具體命令類 TVOpenCommand(電視機打開命令類)

    public class TVOpenCommand implements AbstractCommand {
    
        private Televison tv;
    
        public TVOpenCommand() {
    
            tv = new Televison();
        }
    
        @Override
        public void execute() {
            tv.open();
        }
    }
  4. 具體命令類 TVCloseCommand(電視機關閉命令類)

    public class TVCloseCommand implements AbstractCommand {
    
        private Televison tv;
    
        public TVCloseCommand() {
    
            tv = new Televison();
        }
    
        @Override
        public void execute() {
            tv.close();
        }
    }
  5. 具體命令類 TVChangeCommand(電視機頻道切換命令類)

    public class TVChangeCommand implements AbstractCommand {
    
        private Televison tv;
    
        public TVChangeCommand() {
    
            tv = new Televison();
        }
    
        @Override
        public void execute() {
            tv.changeChannel();
        }
    }
  6. 調用者類 Controller(遙控器類)

    public class Controller {
    
        private AbstractCommand openCommand, closeCommand, changeCommand;
    
        public Controller(AbstractCommand openCommand, AbstractCommand closeCommand, AbstractCommand changeCommand) {
            this.openCommand = openCommand;
            this.closeCommand = closeCommand;
            this.changeCommand = changeCommand;
        }
    
        public void open() {
            openCommand.execute();
        }
    
        public void change() {
            changeCommand.execute();
        }
    
        public void close() {
            closeCommand.execute();
        }
    }
  7. 客戶端測試類 Client

    public class Client {
    
        public static void main(String[] args) {
    
            AbstractCommand openCommand, closeCommand, changeCommand;
    
            openCommand = new TVOpenCommand();
            closeCommand = new TVCloseCommand();
            changeCommand = new TVChangeCommand();
    
            Controller controller = new Controller(openCommand, closeCommand, changeCommand);
    
            controller.open();
            controller.change();
            controller.close();
        }
    }
  8. 運行結果


模式優缺點

命令模式優勢:

  1. 下降系統耦合度
  2. 新的命令能夠很容易地加入系統中
  3. 能夠比較容易地設計一個設計一個命令隊列和宏命令(組合命令)
  4. 能夠方便實現對請求的 Undo 和 Redo

命令模式缺點:

  1. 使用命令模式可能致使某些系統有過多的具體命令類

撤銷操做的實現

咱們能夠經過對命令類進行修改使得系統支持撤銷操做和恢復操做,,抽象命令類(AbstractCommand)聲明一個 undo() 方法

public interface AbstractCommand {
    public void undo();
    public void execute();
}

具體命令類(ConcreteCommand)實如今抽象命令類(AbstractCommand)中聲明的 execute() 和 undo() 方法

public class ConcreteCommand implements AbstractCommand {

    private Receiver receiver;

    public ConcreteCommand() {
        receiver = new Receiver();
    }

    @Override
    public void execute() {
        receiver.method;
    }
    
    @Override
    public void undo() {
        // 撤銷 execute() 操做
    }
}

調用者(Invoker)照常引用一個抽象命令 AbstractCommand 類型的對象 command,經過該 command 對象間接調用接收者 Receiver 類的業務方法

public class Invoker {

    private AbstractCommand command;

    public Invoker(AbstractCommand command) {
        this.openCommand = openCommand;
    }

    public void method() {
        command.execute();
    }
    
    public void undo() {
        // 撤銷操做
    }
}

上述實例只能實現一步撤銷操做,由於沒有保存命令對象的歷史狀態,能夠經過引入一個命令集合或其餘方式來存儲中間狀態,從而實現屢次撤銷操做


宏命令

宏命令又稱組合命令,它是命令模式和組合模式聯用的產物。宏命令也是一個具體命令,不過它包含了對其餘命令對象的引用,在調用宏命令的 execute() 方法,將遞歸調用它所包含的每一個成員命令的 execute() 方法。一個宏命令的成員對象能夠是簡單命令,也能夠繼續是宏命令。


上一站:軟件設計模式學習(十七)職責鏈模式
下一站:軟件設計模式學習(十九)解釋器模式

相關文章
相關標籤/搜索