天天看點

設計模式之七——指令模式(Command)正式定義執行個體講解模式總結

正式定義

指令模式将“請求”封裝成對象,以便使用不同的請求、隊列或者日志來參數化其他對象。指令模式也支援可撤銷的操作。

執行個體講解

讓我們通過一個示範例子來對指令模式有一個直覺的了解。

假設我們要設計一個遙控器,這個遙控器具有一個可程式設計的插槽,插槽有對應的開關按鈕,并且還有一個整體的撤銷按鈕。假設我們現在要用這個遙控器來控制車庫門,車庫門類如下所示:

package com.star.command;

/**
 * 車庫門類,它具有開啟、關閉、開燈、關燈等功能
 */
public class GarageDoor {
    public void up(){ System.out.println("Garage Door up");}
    public void down(){ System.out.println("Garage Door down"); }
    public void stop(){ System.out.println("Garage Door stop"); }
    public void lightOn(){ System.out.println("Garage Door LightOn");}
    public void lightOff(){ System.out.println("Garage Door LightOff");}
}
           

車庫門是接受指令的對象,我們稱它為指令的接收者。接下來我們建立一個車庫門打開的指令類,這個對象接受一個車庫門對象,調用它的up方法來打開車庫門,同時調用lightOn方法打開車庫的燈。

package com.star.command;

/**
 * 車庫門打開指令。
 * 是一個Command。
 * @see Command
 * 接收一個車庫門對象,并調用其功能方法來實作自己的execute方法
 */
public class GarageDoorOpenCommand implements Command {
    GarageDoor garageDoor;

    public GarageDoorOpenCommand(GarageDoor garageDoor) {
        this.garageDoor = garageDoor;
    }

    public void execute() {
        garageDoor.up();
        garageDoor.lightOn();

    }
}
           

GarageDoorOpenCommand是一個具體的指令,同時我們注意到GarageDoorOpenCommand實作了Command接口,Command接口内容如下:

package com.star.command;
/**
 * 指令接口。
 * 定義了統一的execute()方法,所有的指令類都需要繼承該接口
 */
public interface Command {
    public void execute();
}
           

Command接口是一個抽象的指令,這個接口隻有一個execute方法。讓車庫門打開指令實作一個指令接口是有好處的,這樣可以讓指令的調用者與指令的接收者——車庫門類解耦,以後我們需要增加新的指令的時候,比如說添加一個卧室燈控的指令,那麼隻需要讓這個指令實作Command接口,而不需要修改指令調用者的代碼,這展現了“對拓展開放,對修改關閉”的OO設計原則。

現在我們有了車庫門、車庫門打開指令,現在我們需要一個遙控器來操控這個指令:

package com.star.command;

/**
 * 簡單遙控器類。
 * 該類可以考慮構造成單例模式,畢竟遙控器一個就夠了。
 */
public class SimpleRemoteControl {
    /**
     * 使用command接口,來實作調用者與具體指令的解耦
     */
    Command command;

    //不使用構造器引入command的原因是,構造器隻能在構造對象時使用一次,之後便無法更改。
    public SimpleRemoteControl() {}

    /**
     * setter方法,用于設定指令,并且後面可以再次調用該方法更改command。
     * @param command 需要執行的指令
     */
    public void setCommand(Command command) {
        this.command = command;
    }
    //模拟遙控器按下的操作,調用指令的execute方法。
    public void buttonWasPressed(){
        command.execute();
    }
}
           

這個遙控器持有一個指令的引用,并用setCommand()方法來設定指令。當遙控器被按下,即buttonWasPressed()方法執行時,遙控器會調用傳進來的指令的execute方法完成功能操作。是以遙控器是指令的調用者。

現在,讓我們來測試一下我們的遙控器是否起作用:

package com.star.command;

/**
 * 遙控器測試類
 * 在指令模式中擔當“客戶”這一角色,負責建立指令,并将一個接收者傳入指令中。
 */
public class RemoteControlTest {  //客戶
    public static void main(String[] args) {
        //建立遙控器
        SimpleRemoteControl remoteControl=new SimpleRemoteControl(); //調用者
        //建立一個車庫門對象,也就是指令的接收者
        GarageDoor garageDoor=new GarageDoor();  //接收者
        //建立車庫門打開指令,傳入一個車庫門對象
        GarageDoorOpenCommand command=new GarageDoorOpenCommand(garageDoor); //指令
        //為遙控器設定指令——把指令傳給調用者。
        remoteControl.setCommand(command);
        //按下遙控器
        remoteControl.buttonWasPressed();

    }
}
 /* 輸出為:
        Garage Door up
        Garage Door LightOn

        Process finished with exit code 0
  */
           

在測試類中,我們首先建立了一個調用者,也就是遙控器。接着我們建立了車庫門對象和車庫門打開指令對象,他們分别是接收者和指令。我們将接收者傳遞進了指令中,以便在調用者調用指令時執行具體的操作。然後我們使用setCommand()方法将指令傳遞給了遙控器,最後,按下遙控器的按鈕。從輸出中我們可以看到,當我們按下按鈕的時候,車庫門打開了,車庫燈也打開了。RemoteControlTest類建立了指令和接收者,并将接收者傳入到指令當中。我們稱它為客戶。

模式總結

從上面的例子我們可以看到,我們用一個“打開車庫門指令”加載按鈕插槽,同樣我們可以用setCommand()指令加載另一個指令,如“打開卧室燈指令”。遙控器插槽根本不在乎所擁有的的是什麼指令,隻要該指令對象實作了Command接口就可以了。

指令模式的類圖:

設計模式之七——指令模式(Command)正式定義執行個體講解模式總結

指令模式将“請求”封裝成對象,以便使用不同的請求、隊列或者日志來參數化其他對象。指令模式也支援可撤銷的操作。

完整的執行個體已經上傳到Github,歡迎大家克隆學習。Github項目位址:https://github.com/Dodozhou/DesignPatternLearning

繼續閱讀