請求傳送者與接收者解耦——命令模式(二)
3 完整解決方案
為了降低功能鍵與功能處理類之間的耦合度,讓使用者可以自定義每一個功能鍵的功能,Sunny軟體公司開發人員使用命令模式來設計“自定義功能鍵”模組,其核心結構如圖4所示:
圖4 自定義功能鍵核心結構圖
在圖4中,FBSettingWindow是“功能鍵設定”介面類,FunctionButton充當請求呼叫者,Command充當抽象命令類,MinimizeCommand和HelpCommand充當具體命令類,WindowHanlder和HelpHandler充當請求接收者。完整程式碼如下所示:
import java.util.*; //功能鍵設定視窗類 class FBSettingWindow { private String title; //視窗標題 //定義一個ArrayList來儲存所有功能鍵 private ArrayList<FunctionButton> functionButtons = new ArrayList<FunctionButton>(); public FBSettingWindow(String title) { this.title = title; } public void setTitle(String title) { this.title = title; } public String getTitle() { return this.title; } 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("顯示幫助文件!"); } }
為了提高系統的靈活性和可擴充套件性,我們將具體命令類的類名儲存在配置檔案中,並通過工具類XMLUtil來讀取配置檔案並反射生成物件,XMLUtil類的程式碼如下所示:
import javax.xml.parsers.*; import org.w3c.dom.*; import org.xml.sax.SAXException; import java.io.*; public class XMLUtil { //該方法用於從XML配置檔案中提取具體類類名,並返回一個例項物件,可以通過引數的不同返回不同類名節點所對應的例項 public static Object getBean(int i) { try { //建立文件物件 DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("config.xml")); //獲取包含類名的文字節點 NodeList nl = doc.getElementsByTagName("className"); Node classNode = null; if (0 == i) { classNode = nl.item(0).getFirstChild(); } else { classNode = nl.item(1).getFirstChild(); } String cName = classNode.getNodeValue(); //通過類名生成例項物件並將其返回 Class c = Class.forName(cName); Object obj = c.newInstance(); return obj; } catch(Exception e){ e.printStackTrace(); return null; } } }
配置檔案config.xml中儲存了具體建造者類的類名,程式碼如下所示:
<?xml version="1.0"?>
<config>
<className>HelpCommand</className>
<className>MinimizeCommand</className>
</config>
編寫如下客戶端測試程式碼:
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(); } }
編譯並執行程式,輸出結果如下:
顯示視窗:功能鍵設定 顯示功能鍵: 功能鍵1 功能鍵1 ------------------------------ 點選功能鍵:顯示幫助文件! 點選功能鍵:將視窗最小化至托盤! |
如果需要修改功能鍵的功能,例如某個功能鍵可以實現“自動截圖”,只需要對應增加一個新的具體命令類,在該命令類與螢幕處理者(ScreenHandler)之間建立一個關聯關係,然後將該具體命令類的物件通過配置檔案注入到某個功能鍵即可,原有程式碼無須修改,符合“開閉原則”。在此過程中,每一個具體命令類對應一個請求的處理者(接收者),通過向請求傳送者注入不同的具體命令物件可以使得相同的傳送者對應不同的接收者,從而實現“將一個請求封裝為一個物件,用不同的請求對客戶進行引數化”,客戶端只需要將具體命令物件作為引數注入請求傳送者,無須直接操作請求的接收者。