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)之間建立一個關聯關係,然後將該具體命令類的對象通過設定檔注入到某個功能鍵即可,原有代碼無須修改,符合“開閉原則”。在此過程中,每一個具體命令類對應一個請求的處理者(接收者),通過向請求寄件者注入不同的具體命令對象可以使得相同的寄件者對應不同的接收者,從而實現“將一個請求封裝為一個對象,用不同的請求對客戶進行參數化”,用戶端只需要將具體命令對象作為參數注入請求寄件者,無須直接操作請求的接收者。
【作者:劉偉 http://blog.csdn.net/lovelion】