Java——設計模式(行為型模式)
一、責任鏈模式
二、命令模式
在軟件開發中,我們經常需要向某些對象發送請求(調用其中的某個或某些方法),但是並不知道請求的接收者是誰,也不知道被請求的操作是哪個,此時,我們特別希望能夠以一種松耦合的方式來設計軟件,使得請求發送者與請求接收者能夠消除彼此之間的耦合,讓對象之間的調用關系更加靈活,可以靈活地指定請求接收者以及被請求的操作。命令模式為此類問題提供了一個較為完美的解決方案。命令模式可以將請求發送者和接收者完全解耦,發送者與接收者之間沒有直接引用關系,發送請求的對象只需要知道如何發送請求,而不必知道如何完成請求。命令模式定義如下:
命令模式(Command Pattern):將一個請求封裝為一個對象,從而讓我們可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操作。命令模式是一種對象行為型模式,其別名為動作(Action)模式或事務(Transaction)模式。
命令模式的定義比較復雜,提到了很多術語,例如“用不同的請求對客戶進行參數化”、“對請求排隊”、“記錄請求日誌”、“支持可撤銷操作”等,在後面我們將對這些術語進行一一講解。
命令模式的核心在於引入了命令類,通過命令類來降低發送者和接收者的耦合度,請求發送者只需指定一個命令對象,再通過命令對象來調用請求接收者的處理方法,其結構如圖所示:
Command(抽象命令類):抽象命令類一般是一個抽象類或接口,在其中聲明了用於執行請求的 execut e() 等方法,通過這些方法可以調用請求接收者的相關操作。
ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方 法,它對應具體的接收者對象,將接收者對象的動作綁定其中。在實現 execute() 方法時,將調用接收者對 象的相關操作(Action)。
Invoker(調用者):調用者即請求發送者,它通過命令對象來執行請求。一個調用者並不需要在設計時確定 其接收者,因此它只與抽象命令類之間存在關聯關系。在程序運行時可以將一個具體命令對象註入其中,再 調用具體命令對象的 execute() 方法,從而實現間接調用請求接收者的相關操作。
Receiver(接收者):接收者執行與請求相關的操作,它具體實現對請求的業務處理。
命令模式的本質是對請求進行封裝,一個請求對應於一個命令,將發出命令的責任和執行命令的責任分割開。每 一個命令都是一個操作:請求的一方發出請求要求執行一個操作;接收的一方收到請求,並執行相應的操作。命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求如何被接收、操作是否被執行、何時被執行,以及是怎麽被執行的。
命令模式的關鍵在於引入了抽象命令類,請求發送者針對抽象命令類編程,只有實現了抽象命令類的具體命令才 與請求接收者相關聯。在最簡單的抽象命令類中只包含了一個抽象的execute() 方法,每個具體命令類將一個Receiver 類型的對象作為一個實例變量進行存儲,從而具體指定一個請求的接收者,不同的具體命令類提供了 execute() 方法的不同實現,並調用不同接收者的請求處理方法。
public class A { //功能鍵設置窗口類 class FBSettingWindow { private String title; //窗口標題 //定義一個ArrayList來存儲所有功能鍵 private ArrayList<FunctionButton> functionButtons = new ArrayList<FunctionButton>(); public void addFunctionButton(FunctionButton fb) { functionButtons.add(fb); } public void removeFunctionButton(FunctionButton fb) { functionButtons.remove(fb); } //顯示窗口及功能鍵 public void display() { System.out.println("顯示窗口:" + this.title); System.out.println("顯示功能鍵:"); for (Object obj : functionButtons) { System.out.println(((FunctionButton) obj).getName()); } System.out.println("------------------------------"); } } //功能鍵類:請求發送者 class FunctionButton { private String name; //功能鍵名稱 private Command command; //維持一個抽象命令對象的引用 public FunctionButton(String name) { this.name = name; } public String getName() { return this.name; } //為功能鍵註入命令 public void setCommand(Command command) { this.command = command; } //發送請求的方法 public void onClick() { System.out.print("點擊功能鍵:"); command.execute(); } } //抽象命令類 abstract class Command { public abstract void execute(); } //幫助命令類:具體命令類 class HelpCommand extends Command { private HelpHandler hhObj; //維持對請求接收者的引用 public HelpCommand() { hhObj = new HelpHandler(); } //命令執行方法,將調用請求接收者的業務方法 public void execute() { hhObj.display(); } } //最小化命令類:具體命令類 class MinimizeCommand extends Command { private WindowHanlder whObj; //維持對請求接收者的引用 public MinimizeCommand() { whObj = new WindowHanlder(); } //命令執行方法,將調用請求接收者的業務方法 public void execute() { whObj.minimize(); } } //窗口處理類:請求接收者 class WindowHanlder { public void minimize() { System.out.println("將窗口最小化至托盤!"); } } //幫助文檔處理類:請求接收者 class HelpHandler { public void display() { System.out.println("顯示幫助文檔!"); } } } // 客戶端調用 class Client { public static void main(String args[]) { FBSettingWindow fbsw = new FBSettingWindow("功能鍵設置"); FunctionButton fb1, fb2; fb1 = new FunctionButton("功能鍵1"); fb2 = new FunctionButton("功能鍵1"); Command command1, command2; //通過讀取配置文件和反射生成具體命令對象 command1 = (Command) XMLUtil.getBean(0); command2 = (Command) XMLUtil.getBean(1); //將命令對象註入功能鍵 fb1.setCommand(command1); fb2.setCommand(command2); fbsw.addFunctionButton(fb1); fbsw.addFunctionButton(fb2); fbsw.display(); //調用功能鍵的業務方法 fb1.onClick(); fb2.onClick(); } }
如果需要修改功能鍵的功能,例如某個功能鍵可以實現“自動截屏”,只需要對應增加一個新的具體命令類,在該命令類與屏幕處理者(ScreenHandler)之間創建一個關聯關系,然後將該具體命令類的對象通過配置文件註 入到某個功能鍵即可,原有代碼無須修改,符合“開閉原則”。在此過程中,每一個具體命令類對應一個請求的 處理者(接收者),通過向請求發送者註入不同的具體命令對象可以使得相同的發送者對應不同的接收者,從而 實現“將一個請求封裝為一個對象,用不同的請求對客戶進行參數化”,客戶端只需要將具體命令對象作為參數 註入請求發送者,無須直接操作請求的接收者。
Java——設計模式(行為型模式)