定義:
????????命令模式是一個高內聚的模式,其定義為:Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.(將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數化,對請 求排隊或者記錄請求日志,可以提供命令的撤銷和恢復功能。)
命令模式通用類圖
????????命令設計模式的核心思想是“封裝請求”。它把每個具體的操作(如打開文件、刪除數據、執行計算等)都封裝成一個獨立的命令對象。這些命令對象實現統一的接口,包含執行操作的方法。請求的發送者(如用戶界面的按鈕點擊事件)只需要與命令對象交互,而無需知道具體的操作是由哪個接收者(如文件系統模塊、數據庫操作類)執行的。這樣,系統可以方便地對命令進行組合、管理和擴展,例如實現命令的撤銷、重做、隊列化執行等功能 。
角色:
責任鏈模式包含以下幾個核心角色:
(一)命令接口(Command)
????????命令接口定義了執行命令的抽象方法execute(),所有具體命令類都必須實現該接口。通過統一的接口,使得不同類型的命令可以被一致地處理。
(二)具體命令類(Concrete Command)
????????具體命令類是命令接口的實現類,它持有一個接收者對象的引用,并在execute()方法中調用接收者的相關方法,完成具體的操作。例如,“打開文件” 命令類會調用文件系統類的打開文件方法。
(三)接收者(Receiver)
????????接收者是真正執行命令操作的對象,它包含了具體的業務邏輯。每個具體命令類都會關聯一個接收者,通過調用接收者的方法來實現命令的功能。
(四)調用者(Invoker)
????????調用者也稱為請求發送者,它持有命令對象的引用,并通過調用命令對象的execute()方法來觸發命令的執行。調用者不關心命令的具體實現,只負責發起請求。例如,遙控器上的按鈕就是調用者,它觸發對應的命令對象執行操作。
(五)客戶端(Client)
????????客戶端負責創建具體命令對象和調用者對象,并將命令對象與調用者進行關聯。客戶端決定了哪些命令會被執行,以及如何組織這些命令 。
代碼示例:
假設我們有一個遙控器,可以控制電視和電燈的開關,使用命令設計模式實現該場景。
// 接收者:電視類
public class Television {public void turnOn() {System.out.println("電視已打開");}public void turnOff() {System.out.println("電視已關閉");}}
// 接收者:電燈類
public class Light {public void turnOn() {System.out.println("電燈已打開");}public void turnOff() {System.out.println("電燈已關閉");}}
// 命令接口
public interface Command {void execute();}
// 具體命令類:打開電視命令
public class TurnOnTelevisionCommand implements Command {private Television television;public TurnOnTelevisionCommand(Television television) {this.television = television;}@Overridepublic void execute() {television.turnOn();}}
// 具體命令類:關閉電視命令
public class TurnOffTelevisionCommand implements Command {private Television television;public TurnOffTelevisionCommand(Television television) {this.television = television;}@Overridepublic void execute() {television.turnOff();}}
// 具體命令類:打開電燈命令
public class TurnOnLightCommand implements Command {private Light light;public TurnOnLightCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}}
// 具體命令類:關閉電燈命令
public class TurnOffLightCommand implements Command {private Light light;public TurnOffLightCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}}
// 調用者:遙控器類
public class RemoteControl {private Command onCommand;private Command offCommand;public RemoteControl(Command onCommand, Command offCommand) {this.onCommand = onCommand;this.offCommand = offCommand;}public void pressOnButton() {onCommand.execute();}public void pressOffButton() {offCommand.execute();}}
// 客戶端代碼
public class CommandPatternClient {public static void main(String[] args) {Television television = new Television();Light light = new Light();Command turnOnTelevisionCommand = new TurnOnTelevisionCommand(television);Command turnOffTelevisionCommand = new TurnOffTelevisionCommand(television);Command turnOnLightCommand = new TurnOnLightCommand(light);Command turnOffLightCommand = new TurnOffLightCommand(light);RemoteControl tvRemote = new RemoteControl(turnOnTelevisionCommand, turnOffTelevisionCommand);RemoteControl lightRemote = new RemoteControl(turnOnLightCommand, turnOffLightCommand);System.out.println("------ 操作電視 ------");tvRemote.pressOnButton();tvRemote.pressOffButton();System.out.println("\n------ 操作電燈 ------");lightRemote.pressOnButton();lightRemote.pressOffButton();}}
優點?:
1、解耦發送者和接收者:命令模式將請求的發送者和接收者分離,降低了兩者之間的耦合度,使代碼結構更加清晰,易于維護和擴展。
2、支持靈活的命令組合:可以將多個命令組合成復合命令,實現復雜的操作邏輯。例如,在游戲中可以將移動、攻擊等多個命令組合成一個連招命令。
3、方便實現撤銷和重做:通過在命令對象中添加撤銷和重做方法,可以輕松實現操作的撤銷和重做功能,提升用戶體驗。
4、支持請求的隊列化和日志記錄:命令對象可以方便地被放入隊列中進行管理,同時記錄命令的執行信息,實現操作的日志記錄和審計。
缺點:
1、增加類的數量:命令設計模式需要定義命令接口、具體命令類等多個類,會增加系統的類數量,使代碼結構變得復雜。
2、可能導致過度設計:對于簡單的請求操作,使用命令模式可能會顯得過于繁瑣,增加不必要的開發成本。在這種情況下,直接調用接收者的方法可能更加簡單高效。
使用場景:?
(一)需要支持撤銷和重做功能
????????由于命令對象封裝了操作的細節,我們可以在命令類中添加undo()和redo()方法,方便實現撤銷和重做操作。例如,文本編輯器中的撤銷 / 重做功能就可以通過命令模式實現。
(二)請求排隊或延遲執行
????????可以將命令對象放入隊列中,按照一定的順序執行,或者在合適的時機延遲執行。例如,在多線程任務調度、數據庫事務處理等場景中,命令模式可以很好地管理請求的執行順序。
(三)請求的發送者和接收者解耦
????????當請求的發送者和接收者之間存在復雜的依賴關系,或者需要降低它們之間的耦合度時,命令模式可以將請求封裝成對象,使兩者之間的關系更加清晰。例如,在圖形界面應用中,按鈕點擊事件(發送者)和具體的業務邏輯(接收者)可以通過命令對象進行解耦。
(四)實現日志記錄和審計功能
????????通過記錄命令對象的執行信息,可以實現操作日志的記錄,方便后續的審計和故障排查。例如,在銀行系統中,每一筆交易操作都可以封裝成命令對象,記錄交易詳情和操作時間。
????????命令設計模式通過將請求封裝為對象,為我們提供了一種強大而靈活的編程方式。它有效解耦了請求的發送者和接收者,使系統更易于維護和擴展,同時支持撤銷、重做、隊列化等高級功能。在實際開發中,當遇到需要處理復雜請求、實現操作管理和擴展功能的場景時,不妨考慮使用命令設計模式。但也要注意避免過度設計,根據具體需求選擇合適的解決方案。掌握命令設計模式,將為你的代碼設計帶來更多的可能性和靈活性。