文章目錄
- 1. 什么是命令模式?
- 2. 為什么需要命令模式?
- 3. 命令模式的核心概念
- 4. 命令模式的結構
- 5. 命令模式的基本實現
- 5.1 簡單的燈光控制示例
- 5.2 家電控制示例
- 6. 帶有撤銷功能的命令模式
- 6.1 修改命令接口
- 6.2 實現可撤銷的燈光命令
- 6.3 實現可撤銷的風扇命令
- 6.4 修改調用者,支持撤銷功能
- 6.5 客戶端代碼演示
- 7. 宏命令實現
- 7.1 實現宏命令
- 7.2 客戶端代碼演示
- 8. 命令隊列實現
- 8.1 命令隊列類
- 8.2 客戶端代碼演示
- 9. 命令日志與恢復
- 9.1 可序列化的命令接口
- 9.2 命令日志管理器
- 10. 命令模式在Java中的實際應用
- 10.1 Swing中的Action
- 10.2 Runnable接口
- 10.3 任務調度系統
- 10.4 Java 8 Lambda表達式
- 11. 命令模式與其他設計模式的結合
- 11.1 命令模式與組合模式
- 11.2 命令模式與備忘錄模式
- 11.3 命令模式與策略模式
- 12. 命令模式的優缺點
- 12.1 優點
- 12.2 缺點
- 13. 命令模式的適用場景
- 14. 命令模式的常見問題與解決方案
- 14.1 如何處理大量的命令類?
- 14.2 如何處理命令執行失敗的情況?
- 14.3 如何實現更高效的撤銷/重做功能?
- 15. 總結
- 15.1 核心要點
- 15.2 設計建議
1. 什么是命令模式?
命令模式是一種行為型設計模式,它將請求(命令)封裝為一個對象,從而使你可以使用不同的請求參數化客戶端,隊列或記錄請求日志,以及支持可撤銷的操作。
簡單來說,命令模式就是將"請求"轉化為一個對象,這個對象可以被存儲、傳遞、調用,而且可以在不同的時間點被請求調用,即使發送請求的對象已經不存在。
2. 為什么需要命令模式?
在以下情況下,命令模式特別有用:
- 需要參數化操作:當你需要根據運行時確定的請求參數來執行操作時
- 需要將操作放入隊列:當操作需要排隊執行,或者在不同時間執行時
- 需要支持撤銷/重做功能:當系統需要支持操作的撤銷和重做功能時
- 需要支持事務:當操作需要作為一個事務執行,要么全部完成,要么全部不做
- 需要將發送者與接收者解耦:當請求發送者不需要知道請求如何被處理以及由誰處理時
3. 命令模式的核心概念
命令模式涉及以下幾個核心角色:
-
命令(Command):
- 聲明執行操作的接口
- 通常只有一個執行方法(如
execute()
)
-
具體命令(Concrete Command):
- 實現命令接口
- 通常持有接收者的引用
- 調用接收者的相關操作來完成命令的執行
-
接收者(Receiver):
- 知道如何實施與命令相關的操作
- 任何類都可以作為接收者
-
調用者(Invoker):
- 要求命令對象執行請求
- 不知道命令是如何執行的,也不知道具體的接收者是誰
-
客戶端(Client):
- 創建具體的命令對象并設置它的接收者
- 將命令對象交給調用者
4. 命令模式的結構
命令模式的UML類圖如下:
+----------------+ +----------------+
| Invoker |------>| Command |
+----------------+ +----------------+| execute() |+----------------+↑|+----------------+|ConcreteCommand |+----------------+| execute() |+----------------+||v+----------------+| Receiver |+----------------+| action() |+----------------+
5. 命令模式的基本實現
5.1 簡單的燈光控制示例
下面是一個簡單的燈光控制示例,展示了命令模式的基本實現。
首先,定義命令接口:
// 命令接口
public interface Command {void execute();
}
然后,定義接收者類(燈):
// 接收者類
public class Light {private String location;public Light(String location) {this.location = location;}public void turnOn() {System.out.println(location + " 燈已打開");}public void turnOff() {System.out.println(location + " 燈已關閉");}
}
接著,定義具體命令類:
// 打開燈的命令
public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}
}// 關閉燈的命令
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}
}
然后,定義調用者類(遠程控制器):
// 調用者類
public class RemoteControl {private Command command;public void setCommand(Command command) {this.command = command;}public void pressButton() {command.execute();}
}
最后,客戶端代碼:
public class CommandPatternDemo {public static void main(String[] args) {// 創建接收者Light livingRoomLight = new Light("客廳");Light kitchenLight = new Light("廚房");// 創建具體命令Command livingRoomLightOn = new LightOnCommand(livingRoomLight);Command livingRoomLightOff = new LightOffCommand(livingRoomLight);Command kitchenLightOn = new LightOnCommand(kitchenLight);Command kitchenLightOff = new LightOffCommand(kitchenLight);// 創建調用者RemoteControl remote = new RemoteControl();// 使用遠程控制器打開客廳燈remote.setCommand(livingRoomLightOn);remote.pressButton();// 使用遠程控制器關閉客廳燈remote.setCommand(livingRoomLightOff);remote.pressButton();// 使用遠程控制器打開廚房燈remote.setCommand(kitchenLightOn);remote.pressButton();// 使用遠程控制器關閉廚房燈remote.setCommand(kitchenLightOff);remote.pressButton();}
}
輸出結果:
客廳 燈已打開
客廳 燈已關閉
廚房 燈已打開
廚房 燈已關閉
5.2 家電控制示例
我們可以擴展上面的例子,添加更多種類的家電控制:
// 音響系統接收者
public class StereoSystem {private String location;public StereoSystem(String location) {this.location = location;}public void on() {System.out.println(location + " 音響已打開");}public void off() {System.out.println(location + " 音響已關閉");}public void setCD() {System.out.println(location + " 音響已設置為CD播放模式");}public void setVolume(int volume) {System.out.println(location + " 音響音量已設置為 " + volume);}
}// 電風扇接收者
public class Fan {private String location;public Fan(String location) {this.location = location;}public void on() {System.out.println(location + " 電風扇已打開");}public void off() {System.out.println(location + " 電風扇已關閉");}public void setHigh() {System.out.println(location + " 電風扇已設置為高速");}public void setMedium() {System.out.println(location + " 電風扇已設置為中速");}public void setLow() {System.out.println(location + " 電風扇已設置為低速");}
}
然后,為這些家電創建對應的命令:
// 打開音響的命令
public class StereoOnWithCDCommand implements Command {private StereoSystem stereo;public StereoOnWithCDCommand(StereoSystem stereo) {this.stereo = stereo;}@Overridepublic void execute() {stereo.on();stereo.setCD();stereo.setVolume(11);}
}// 關閉音響的命令
public class StereoOffCommand implements Command {private StereoSystem stereo;public StereoOffCommand(StereoSystem stereo) {this.stereo = stereo;}@Overridepublic void execute() {stereo.off();}
}// 打開風扇并設置高速的命令
public class FanHighCommand implements Command {private Fan fan;public FanHighCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.on();fan.setHigh();}
}// 關閉風扇的命令
public class FanOffCommand implements Command {private Fan fan;public FanOffCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.off();}
}
修改調用者類,支持多個按鈕:
// 多按鈕遙控器
public class MultiButtonRemote {private Command[] onCommands;private Command[] offCommands;public MultiButtonRemote(int slotCount) {onCommands = new Command[slotCount];offCommands = new Command[slotCount];// 初始化所有命令為空命令對象,避免空指針異常Command noCommand = new NoCommand(); // NoCommand是一個空實現for (int i = 0; i < slotCount; i++) {onCommands[i] = noCommand;offCommands[i] = noCommand;}}public void setCommand(int slot, Command onCommand, Command offCommand) {onCommands[slot] = onCommand;offCommands[slot] = offCommand;}public void pressOnButton(int slot) {onCommands[slot].execute();}public void pressOffButton(int slot) {offCommands[slot].execute();}
}// 空命令 - 用于初始化,避免空指針異常
public class NoCommand implements Command {@Overridepublic void execute() {// 什么也不做}
}
客戶端代碼:
public class HomeAutomationDemo {public static void main(String[] args) {// 創建接收者Light livingRoomLight = new Light("客廳");Light kitchenLight = new Light("廚房");StereoSystem stereo = new StereoSystem("客廳");Fan ceilingFan = new Fan("臥室");// 創建具體命令Command livingRoomLightOn = new LightOnCommand(livingRoomLight);Command livingRoomLightOff = new LightOffCommand(livingRoomLight);Command kitchenLightOn = new LightOnCommand(kitchenLight);Command kitchenLightOff = new LightOffCommand(kitchenLight);Command stereoOnWithCD = new StereoOnWithCDCommand(stereo);Command stereoOff = new StereoOffCommand(stereo);Command fanHigh = new FanHighCommand(ceilingFan);Command fanOff = new FanOffCommand(ceilingFan);// 創建多按鈕遙控器MultiButtonRemote remote = new MultiButtonRemote(4);// 設置每個插槽對應的命令remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);remote.setCommand(1, kitchenLightOn, kitchenLightOff);remote.setCommand(2, stereoOnWithCD, stereoOff);remote.setCommand(3, fanHigh, fanOff);// 測試按鈕System.out.println("------ 按下第1個按鈕的開按鈕 --