正式定義
指令模式将“請求”封裝成對象,以便使用不同的請求、隊列或者日志來參數化其他對象。指令模式也支援可撤銷的操作。
執行個體講解
讓我們通過一個示範例子來對指令模式有一個直覺的了解。
假設我們要設計一個遙控器,這個遙控器具有一個可程式設計的插槽,插槽有對應的開關按鈕,并且還有一個整體的撤銷按鈕。假設我們現在要用這個遙控器來控制車庫門,車庫門類如下所示:
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接口就可以了。
指令模式的類圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLFJUJClTJ1UUJCJUJxIUJ3UUJGhTJDJUJ1UUJxEUJ4EUJ2UUJ0EUJCJUJ0UUJEJUJxkTJ1UUJvwVbvNmLuRGZ19Gbj5CdrJmLwpWMvpHc3R2bvw1LcpDc0RHaiojIsJye.png)
指令模式将“請求”封裝成對象,以便使用不同的請求、隊列或者日志來參數化其他對象。指令模式也支援可撤銷的操作。
完整的執行個體已經上傳到Github,歡迎大家克隆學習。Github項目位址:https://github.com/Dodozhou/DesignPatternLearning