摘要:本文提出了一種界面設計中的架構模式-界面裝配器模式,它致力于分解界面,將界面和組裝行為解耦,將界面邏輯處理與后臺業務邏輯處理解耦,這樣我們在開發GUI胖客戶端界面應用時可以從眾多的界面控制管理中解脫出來,而專注于我們的后臺業務邏輯的開發。通過該模式,我們可以動態地組裝我們的界面,我們甚至還可以在我們的界面中輕松地插入 transaction 事務或 session 會話管理。
關鍵詞:界面;裝配;解耦;模式
中圖分類號:TP312文獻標識碼:A文章編號:1009-3044(2008)17-21433-02
1 引言
界面設計常常是模式產生的根源,無論是架構模式,還是設計模式,比如 MVC 模式,Observer,Facade 等,也是整個軟件行業向前發展的動力。遺憾的是,即使在軟件技術發達的今天,界面設計仍是軟件設計中的難以突破的瓶頸之一,要將界面進行分解是很困難的,它不像我們的業務邏輯,可以方便地按職責分解到不同的類中去實現,一般的做法只能是在界面元素的事件觸發(比如按鈕點擊事件)時,將輸入數據封裝成一個數據對象傳給后臺的邏輯處理類來處理。在這里我會從一個簡單的設計模型開始,一步步走向一個完整的架構。借此也向大家展示一個架構設計的思維歷程。
2 界面部件裝配
當我們的裝配邏輯很簡單時,我們可以定義一個 assemble() 方法來負責裝配行為。但是當我們的界面需要組裝一系列 EditorComposite 時,就會牽涉到選擇邏輯,選擇邏輯不一定很復雜,但我們還是應該把這種行為從 Editor 中分離出來,這樣 Editor 可以集中精力負責與用戶交互方面的職責,而裝配行為被分配到一個新的類 EditorAssembler 中,這樣做還有一個好處,就是我們一旦有新的 EditorComposite 需要添加時,我們只需要改變 EditorAssembler 的代碼,而不用修改 Editor 的代碼,這就把變化隔離出來,對 Editor 的修改關閉,對裝配行為的擴展開放。這正是面向對象設計領域反復強調的基本原則-開放-封閉原則(Open-Close Principle)。架構如下圖所示:
EditorAssembler:該類處理 EditorComposite 的創建,還包括多個 EditorComposite 的選擇邏輯。這里的選擇邏輯我們可以用 if/else 或 switch/case 來硬編碼,如果邏輯不是很復雜而且今后的修改不會太頻繁的話,用這種方法就足夠了,當然可以考慮將多個 EditorComposite 的裝載信息專門用一個資源/信息類來存儲,這在 EditorComposite 比較多的情況下很有效,這樣每次添加 EditorComposite 就只需要改變這個資源類,這是一個很有用的建模原則(為了簡化我們的核心模型,我在這里不將這個資源類表示出來)。
3 IO 數據流組裝
在一個標準的界面程序中,我們首先會有一組輸出數據,比如按”確認”按鈕之后,我們需要將界面元素上的輸入信息輸出到后臺邏輯類來處理或直接調用好幾個邏輯類分別處理不同的界面元素輸入信息了。我們一般習慣上可能直接將這個數據傳遞到邏輯類來處理。這樣做三個缺點:其一,如果我們的數據讀寫處理要求必須在特定的 context 中才能進行,這樣的話我們不能在界面中直接調用后臺邏輯處理類了,在一些涉及底層(比如協議層)的開發時,經常會碰到只能讀不能寫的情況。其二,UI 的可替代性差,假如我們今后需要一種方案可以在運行時可以替換不同的 UI 但輸出的數據是一樣的,也就是說后臺邏輯處理完全一致,那么這種情況我們就需要每一個 UI 自己去調用后臺邏輯類,重復編碼,而且可能由于程序員的失誤每一個 UI 用了一個邏輯類,從而導致一個完全相同行為的類有了好幾個不一致實現版本,這樣不僅嚴重違反了面向對象設計,而且還可能產生難以預料的 bug,難以維護。其三,UI 的可重用性差,對于上面多個 UI 對應一種邏輯處理的例子,由于 UI 依賴了后臺邏輯類,如果今后要修改邏輯類結構的話,我們就需要修改每一個 UI。如果我們還有一種需求是要支持一個 UI 在不同的環境下需要不同的后臺邏輯類時,我們可能要專門在一個 UI 中設置一個屬性來標識后臺將要使用的邏輯類,這會很復雜。
解決上面幾個缺點只有一種方法,就是將后臺邏輯類與 UI 解耦。如果我們把要處理的輸出數據打包成一個輸出數據對象從界面統一輸出,再由 UI 的調用者決定調用哪一個后臺邏輯類來處理數據,而不是 UI 自己決定調用行為,我們調用 UI 時,可能某些界面元素需要的從環境中動態裝載數據,比如一個下列列表,還有一些我們上一次配置好的數據這次需要更新,也需要將已有數據導入。所以我們需要一個輸入數據對象。這就得到如圖2的模型:
InputDataObject:該類封裝了輸入數據。由 EditorComposite 負責解析這些數據。
OutputDataObject:該類封裝了輸出數據。由 EditorComposite 負責產生這些數據。
Editor 負責傳輸這兩個數據對象。
從上面的模型我們可以看出 Editor 類其實相當于一個 Facade,所有的界面與用戶的交互都由它負責集中調度管理,Editor 會將裝配行為分配給 EditorAssembler 類來處理,它還負責臨時存儲輸入輸出數據,當然如果我們有類似 transaction 或 session 之類的處理會由 Editor 委派到別的相關類去處理。應用 Facade 設計模式,我們可以給 Editor 改個名字叫 EditorFacade,這樣更能體現設計者的意圖,千萬不要忽視類的命名,設計是一門嚴肅的科學,每一個細節我們都不能茍且,對架構的設計更要嚴謹。命名可以起到溝通的作用,還能起到提醒的功能,EditorFacade 提醒我們以后要給它添加新的行為是記住它是一個 Facade,不能將不相干的職責分配進來。
4 結束語
本文所講述的界面裝配器模式為我們提供了將界面和組裝行為解耦,將界面邏輯處理與領域邏輯處理解耦的價值觀,在 GUI 胖客戶端型界面中可以大量應用,當然,任何模式,不管是設計模式還是架構模式,都有它的適用性,只有合適的,沒有絕對的優劣與好壞之分。
參考文獻:
[1] GOF.Design Patterns:Elements of Reusable Object-Oriented software[M].機械工業出版社,2000:175-207.
[2] Martin Fowler.Patterns of Enterprise Application Architecture[M].機械工業出版社,2004:274-310.
[3] 閻宏.Java與模式[M].電子工業出版社,2002:471-490.
注:本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文