設(shè)計模式學(xué)習(xí)專欄五--------命令模式
場景
設(shè)計一個智能遙控器,遙控器上有7個插槽 , 每個插槽(某設(shè)備具體供應(yīng)商)對應(yīng)兩個按鈕 on , off ,以及一個全局的撤銷操作undo.
較差的實(shí)現(xiàn)方式
判斷每個插槽對應(yīng)的具體廠商是誰, 然后做出對應(yīng)的動作
if(slot1 == Light){
light.on()
}else if(slot1 == Hottub){
hottub.on()
}else if(slot1 == TV){
tv.on();
}
...
- 出現(xiàn)的問題
- 遙控器和具體的設(shè)備廠商耦合到了一起 , 遙控器需要 認(rèn)識 某個插槽當(dāng)前對應(yīng)設(shè)備的廠商
- 當(dāng)加入/刪除 新的設(shè)備廠商時, 當(dāng)前代碼都需要進(jìn)行改動
如何解決
對象村餐廳的例子
- 一張訂單封裝了 準(zhǔn)備餐點(diǎn)的請求
- 女招待的工作是接收訂單 , 然后調(diào)用訂單的OrderUp()方法 , 女招待不需要擔(dān)心訂單的內(nèi)容是什么, 或者由誰來準(zhǔn)備餐點(diǎn) , 她只需要指導(dǎo),訂單由一個OrderUp()方法可以調(diào)用即可.
- 快餐廚師具有準(zhǔn)備餐點(diǎn)的知識 . 他只要看到訂單就知道如何準(zhǔn)備餐點(diǎn) . 廚師和女招侍之間從不需要直接溝通.
把餐廳想象成設(shè)計模式的一種模型 , 這個模型允許通過"封裝請求的命令"(訂單)將 "發(fā)出請求的對象"(女招待, "訂單來啦") 和 "接收與執(zhí)行這些請求的對象"(廚師) 分隔開 .
從餐廳到命令模式
命令模式總覽
定義:
將請求封裝成對象(訂單)
,將發(fā)出請求
的對象(服務(wù)員/按鈕)和執(zhí)行請求
的對象(廚師/具體供應(yīng)商)解耦 , 也可以支持撤銷
操作
- 類圖
-
模式的理解
-
角色
- 封裝請求的命令對象(訂單) : 一個命令對象通過在特定接收者上綁定一組動作來封裝一個請求
receiver.action1()
, 該對象之暴露一個execute()方法, 當(dāng)此方法被調(diào)用時, 接收者就會進(jìn)行對應(yīng)的動作 . 從女招待的角度來看, 它不需要知道具體哪個 接收者進(jìn)行了什么動作,只知道如果調(diào)用execute()方法 , 請求的目的就能達(dá)到了 - 發(fā)出請求的對象(女招待) : ①負(fù)責(zé)接收命令對象 ②在合適的時候調(diào)用命令對象的execute()方法
- 執(zhí)行請求的對象(廚師) : 真正執(zhí)行請求的接收者
- 封裝請求的命令對象(訂單) : 一個命令對象通過在特定接收者上綁定一組動作來封裝一個請求
-
細(xì)節(jié)
-
"撤銷操作" 只需在Invocker中定義變量
Command undoCommand
保存撤銷操作即可 . 如果要聯(lián)系撤銷, 則可以用棧
的形式存儲執(zhí)行過的命令public class LightOnCommand implements Command { Light light; public LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); } public void undo() { light.off(); } }
public class RemoteControlWithUndo { Command[] onCommands; Command[] offCommands; Command undoCommand; //記錄上一次命令 public RemoteControlWithUndo() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for(int i=0;i<7;i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } public void setCommand(int slot, Command onCommand, Command offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot) { onCommands[slot].execute(); undoCommand = onCommands[slot]; //記錄上一次命令 } public void offButtonWasPushed(int slot) { offCommands[slot].execute(); undoCommand = offCommands[slot]; //記錄上一次命令 } public void undoButtonWasPushed() { undoCommand.undo(); //執(zhí)行撤銷命令 } }
-
-
- "Party模式" : 封裝一次請求中的批量操作 (按下一個按鈕,同時弄暗燈光 , 打開音響和電視,設(shè)置好DVD)
```java
public class MacroCommand implements Command {
Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
public void execute() {
for (int i = 0; i < commands.length; i++) {
commands[i].execute();
}
}
}
```
-
使用場景
- 隊列請求 (日志安排 , 線程池 , 工作隊列)
- 以線程池為例 , 將線程需要執(zhí)行的動作封裝進(jìn)Runnable對象中
- 線程池 以 阻塞隊列存儲Runnable對象
- 當(dāng)線程空閑時 , 從隊列中取出Runnable對象 , 并執(zhí)行Runnable中重寫的Run()方法
- 總結(jié) : 即線程池中的線程不需要知道具體的執(zhí)行者是誰,干了什么,只需要在空閑時從隊列中取出命令對象Runnable并調(diào)用run()方法即可
- 日志請求
- 當(dāng)每個命令被執(zhí)行時, 會被儲存在磁盤中
- 在系統(tǒng)死機(jī)后, 重新加載存儲的命令,并以準(zhǔn)備的次序執(zhí)行
- 隊列請求 (日志安排 , 線程池 , 工作隊列)
案例代碼部分
-
命令對象Command
public interface Command { public void execute(); public void undo(); }
-
具體命令對象--開燈
public class LightOnCommand implements Command { Light light; int level; public LightOnCommand(Light light) { this.light = light; } public void execute() { level = light.getLevel(); light.on(); } public void undo() { light.dim(level); } }
-
具體命令對象 --關(guān)燈
public class LightOffCommand implements Command { Light light; int level; public LightOffCommand(Light light) { this.light = light; } public void execute() { level = light.getLevel(); light.off(); } public void undo() { light.dim(level); } }
-
設(shè)備廠商 -- Receiver
public class Light { String location; int level; public Light(String location) { this.location = location; } public void on() { level = 100; System.out.println("Light is on"); } public void off() { level = 0; System.out.println("Light is off"); } public void dim(int level) { this.level = level; if (level == 0) { off(); } else { System.out.println("Light is dimmed to " + level + "%"); } } public int getLevel() { return level; } }
-
調(diào)用者Invoker --遠(yuǎn)程遙控器
public class RemoteControlWithUndo { Command[] onCommands; Command[] offCommands; Command undoCommand; public RemoteControlWithUndo() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for(int i=0;i<7;i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } public void setCommand(int slot, Command onCommand, Command offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot) { onCommands[slot].execute(); undoCommand = onCommands[slot]; } public void offButtonWasPushed(int slot) { offCommands[slot].execute(); undoCommand = offCommands[slot]; } public void undoButtonWasPushed() { undoCommand.undo(); } }
-
主程序
public class RemoteLoader { public static void main(String[] args) { RemoteControlWithUndo remoteControl = new RemoteControlWithUndo(); Light livingRoomLight = new Light("Living Room"); LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight); remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.onButtonWasPushed(0); remoteControl.offButtonWasPushed(0); System.out.println(remoteControl); remoteControl.undoButtonWasPushed(); //撤銷操作 System.out.println(remoteControl); //此時應(yīng)該是開燈狀態(tài) } }
-
輸出結(jié)果
Light is on Light is off ------ Remote Control ------- [slot 0] headfirst.designpatterns.command.undo.LightOnCommand headfirst.designpatterns.command.undo.LightOffCommand [slot 1] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 2] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 3] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 4] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 5] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 6] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [undo] headfirst.designpatterns.command.undo.LightOffCommand Light is dimmed to 100% //撤銷操作 ==> 開燈狀態(tài) ------ Remote Control ------- [slot 0] headfirst.designpatterns.command.undo.LightOnCommand headfirst.designpatterns.command.undo.LightOffCommand [slot 1] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 2] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 3] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 4] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 5] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [slot 6] headfirst.designpatterns.command.undo.NoCommand headfirst.designpatterns.command.undo.NoCommand [undo] headfirst.designpatterns.command.undo.LightOffCommand
參考
? 書籍: HeadFirst設(shè)計模式
? 代碼參考地址: 我就是那個地址