設計模式—–命令模式
個人博客,想要搭建個人博客的可以進來看看: http://www.ioqian.top/
命令模式 , 將請求封裝成對象,以便使用不同的請求,隊列或日誌來參數化其他對象,命令模式也支持可撤銷的操作
設計模式系列源碼: https://github.com/liloqian/DesiginModeDemo
背景
現在有一個遙控器,上面有1個按鈕,我們要根據不同的請求來控制不同的電器,比如說可以控制風扇,點燈等,這裏我們就可以引入命令模式
在面向對象的程序設計中,一個對象調用另一個對象,一般情況下的調用過程是:創建目標對象實例;設置調用參數;調用目標對象的方法。
但在有些情況下有必要使用一個專門的類對這種調用過程加以封裝,我們把這種專門的類稱作command類。
1.下面先大概看一下UML
- Command ,是所有命令的一個接口,調用Command中的execute()方法就可以讓接受者進行相關的動作
- Receiver , 動作的最終執行者 ,比如我們背景中的風扇 , 電燈,打開風扇,打開電燈等動作最終只可能由電燈風扇自身實現
- CreateCommand ,持有Receiver的引用,繼承了Command接口,在Command接口的execute()方法中直接調用Receiver的具體動作
- Invoker , 調用者,持有一個命令接口,當客戶調用時直接調用命令接口的execute()方法
2.下面來看具體的代碼
Receiver,這裏實現了2個命令接收者,分別是風扇和電燈
/**Receiver 具體的命令接收者,接受者指導如何進行必要的工作,也是真正執行命令的方法*/
public class Light {
//實現了這個方法都可以成爲命令接收者,這個方法是真正執行命令的方法,對應上圖中的action()方法
public void on(){
System.out.println(this.toString()+": turn on the light");
}
@Override
public String toString() {
return "I-am-light";
}
}
/**另外一個接受者*/
public class Fan {
public void on(){
System.out.println(this.toString()+": turn on the fan");
}
@Override
public String toString() {
return "I-am-fan";
}
}
Command接口
/**命令接口,所以的遙控器控制的電器命令都要實現這個接口*/
public interface Command {
public void execute();
}
CreateCommand,實現了Command接口的具體命令,持有接受者Receiver的引用
/**CreateCommand*/
public class LightCommand implements Command {
//持有一份接受者Receiver中Light的引用
Light light ;
public LightCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
/**和上面的一樣*/
public class FanCommand implements Command {
Fan fan;
public FanCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.on();
}
}
Invoker
/**Invoker 持有一個命令對象,當點擊按鈕時就執行命令對象的方法*/
public class SimpleRemoteControl {
//命令對象
Command command;
//點擊按鈕後調用此方法去調用命令對象實現的方法
public void buttonPressed(){
command.execute();
}
public void setCommand(Command command) {
this.command = command;
}
}
Client ,直接在Main測試中進行
public class Main {
public static void main(String[] args) {
//實例化一個命令調用者
SimpleRemoteControl remote = new SimpleRemoteControl();
//電燈命令
LightCommand lightOn = new LightCommand(new Light());
//設置電燈命令,點擊按鈕去執明LightCommand的方法
remote.setCommand(lightOn);
remote.buttonPressed();
//下面的和上面的一樣
FanCommand fanOn = new FanCommand(new Fan());
remote.setCommand(fanOn);
remote.buttonPressed();
}
}
//結果 , 我們通過傳入不同的命令參數可以執行不同的請求
I-am-light: turn on the light
I-am-fan: turn on the fan
Process finished with exit code 0
上面的模型中也可以不必一定要有Receiver,可以把具體的動作放在ConcreteCommand中
3.命令模式的用途,隊列請求
新建一個專門處理請求對象的線程,從隊列獲取請求對象(就是請求封裝的對象),進行處理;在其他線程中把請求封裝成對象加入這個隊列
4.命令模式的要點
- 命令模式將要發出請求的對象和執行請求的對象解耦,執行請求的對象不關心請求的具體內容,只要把具體的請求封裝成請求的對象就可以了
- 被解耦的兩者之間是通過命令對象進行溝通的,命令對象封裝了接收者和一個或一組動作
- 調用者通過調用命令對象的execute()方法使得接受者的動作被執行
- 命令可以被撤銷,做法是實現一個undo()方法來回到execute()之前的狀態,undo()和execute()的具體實現一樣
- 實際中,使用聰明命令對象,而不是把工作給了接受者,就是前面說的,不必一定要有Receiver,可以把具體的動作放在ConcreteCommand中