設(shè)計(jì)模式學(xué)習(xí)專(zhuān)欄五--------命令模式
場(chǎng)景
設(shè)計(jì)一個(gè)智能遙控器,遙控器上有7個(gè)插槽 , 每個(gè)插槽(某設(shè)備具體供應(yīng)商)對(duì)應(yīng)兩個(gè)按鈕 on , off ,以及一個(gè)全局的撤銷(xiāo)操作undo.

較差的實(shí)現(xiàn)方式
判斷每個(gè)插槽對(duì)應(yīng)的具體廠商是誰(shuí), 然后做出對(duì)應(yīng)的動(dòng)作
if(slot1 == Light){
light.on()
}else if(slot1 == Hottub){
hottub.on()
}else if(slot1 == TV){
tv.on();
}
...
- 出現(xiàn)的問(wèn)題
- 遙控器和具體的設(shè)備廠商耦合到了一起 , 遙控器需要 認(rèn)識(shí) 某個(gè)插槽當(dāng)前對(duì)應(yīng)設(shè)備的廠商
- 當(dāng)加入/刪除 新的設(shè)備廠商時(shí), 當(dāng)前代碼都需要進(jìn)行改動(dòng)
如何解決
對(duì)象村餐廳的例子

- 一張訂單封裝了 準(zhǔn)備餐點(diǎn)的請(qǐng)求
- 女招待的工作是接收訂單 , 然后調(diào)用訂單的OrderUp()方法 , 女招待不需要擔(dān)心訂單的內(nèi)容是什么, 或者由誰(shuí)來(lái)準(zhǔn)備餐點(diǎn) , 她只需要指導(dǎo),訂單由一個(gè)OrderUp()方法可以調(diào)用即可.
- 快餐廚師具有準(zhǔn)備餐點(diǎn)的知識(shí) . 他只要看到訂單就知道如何準(zhǔn)備餐點(diǎn) . 廚師和女招侍之間從不需要直接溝通.
把餐廳想象成設(shè)計(jì)模式的一種模型 , 這個(gè)模型允許通過(guò)"封裝請(qǐng)求的命令"(訂單)將 "發(fā)出請(qǐng)求的對(duì)象"(女招待, "訂單來(lái)啦") 和 "接收與執(zhí)行這些請(qǐng)求的對(duì)象"(廚師) 分隔開(kāi) .
從餐廳到命令模式

命令模式總覽
定義:
將請(qǐng)求封裝成對(duì)象(訂單),將發(fā)出請(qǐng)求的對(duì)象(服務(wù)員/按鈕)和執(zhí)行請(qǐng)求的對(duì)象(廚師/具體供應(yīng)商)解耦 , 也可以支持撤銷(xiāo)操作
- 類(lèi)圖

-
模式的理解
-
角色
- 封裝請(qǐng)求的命令對(duì)象(訂單) : 一個(gè)命令對(duì)象通過(guò)在特定接收者上綁定一組動(dòng)作來(lái)封裝一個(gè)請(qǐng)求
receiver.action1(), 該對(duì)象之暴露一個(gè)execute()方法, 當(dāng)此方法被調(diào)用時(shí), 接收者就會(huì)進(jìn)行對(duì)應(yīng)的動(dòng)作 . 從女招待的角度來(lái)看, 它不需要知道具體哪個(gè) 接收者進(jìn)行了什么動(dòng)作,只知道如果調(diào)用execute()方法 , 請(qǐng)求的目的就能達(dá)到了 - 發(fā)出請(qǐng)求的對(duì)象(女招待) : ①負(fù)責(zé)接收命令對(duì)象 ②在合適的時(shí)候調(diào)用命令對(duì)象的execute()方法
- 執(zhí)行請(qǐng)求的對(duì)象(廚師) : 真正執(zhí)行請(qǐng)求的接收者
- 封裝請(qǐng)求的命令對(duì)象(訂單) : 一個(gè)命令對(duì)象通過(guò)在特定接收者上綁定一組動(dòng)作來(lái)封裝一個(gè)請(qǐng)求
-
細(xì)節(jié)
-
"撤銷(xiāo)操作" 只需在Invocker中定義變量
Command undoCommand保存撤銷(xiāo)操作即可 . 如果要聯(lián)系撤銷(xiāo), 則可以用棧的形式存儲(chǔ)執(zhí)行過(guò)的命令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í)行撤銷(xiāo)命令 } }
-
-
- "Party模式" : 封裝一次請(qǐng)求中的批量操作 (按下一個(gè)按鈕,同時(shí)弄暗燈光 , 打開(kāi)音響和電視,設(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();
}
}
}
```
-
使用場(chǎng)景
- 隊(duì)列請(qǐng)求 (日志安排 , 線程池 , 工作隊(duì)列)
- 以線程池為例 , 將線程需要執(zhí)行的動(dòng)作封裝進(jìn)Runnable對(duì)象中
- 線程池 以 阻塞隊(duì)列存儲(chǔ)Runnable對(duì)象
- 當(dāng)線程空閑時(shí) , 從隊(duì)列中取出Runnable對(duì)象 , 并執(zhí)行Runnable中重寫(xiě)的Run()方法
- 總結(jié) : 即線程池中的線程不需要知道具體的執(zhí)行者是誰(shuí),干了什么,只需要在空閑時(shí)從隊(duì)列中取出命令對(duì)象Runnable并調(diào)用run()方法即可
- 日志請(qǐng)求
- 當(dāng)每個(gè)命令被執(zhí)行時(shí), 會(huì)被儲(chǔ)存在磁盤(pán)中
- 在系統(tǒng)死機(jī)后, 重新加載存儲(chǔ)的命令,并以準(zhǔn)備的次序執(zhí)行
- 隊(duì)列請(qǐng)求 (日志安排 , 線程池 , 工作隊(duì)列)
案例代碼部分

-
命令對(duì)象Command
public interface Command { public void execute(); public void undo(); } -
具體命令對(duì)象--開(kāi)燈
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); } } -
具體命令對(duì)象 --關(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(); //撤銷(xiāo)操作 System.out.println(remoteControl); //此時(shí)應(yīng)該是開(kāi)燈狀態(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% //撤銷(xiāo)操作 ==> 開(kāi)燈狀態(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
參考
? 書(shū)籍: HeadFirst設(shè)計(jì)模式
? 代碼參考地址: 我就是那個(gè)地址