有了前面諸多設計模式的基礎,這兒可以提出一個比較特殊的模式MVC。MVC並不屬於GOF 的23個設計模式之列,但是它在GOF的書中作為一個重要的例子被提出來,並給予了很高的評 價。一般的來講,我們認為GOF的23個模式是一些中級的模式,在它下面還可以抽象出一些更 為一般的低層的模式,在其上也可以通過組合來得到一些高級的模式。MVC就可以看作是一些 模式進行組合之後的結果(實際上,MVC的出現要早於設計模式的提出,這而只是對它在設計 模式的基礎上進行在分析)。如果沒有前面的基礎,理解MVC或許會有一些困難。
MVC模式
MVC模式比較的特別,它含義比較的廣,涉及的層面也不僅僅是設計這一塊,不好簡單的 把它歸為設計模式。當然,它主要還是作為一個設計的概念被提到的,而且在Java體系中, MVC有著至關重要的作用。這兒提的是Java中的設計模式,當然不好拉了它不講了。
關於MVC的來龍去脈,這兒就不再講了。這裡主s要講兩個方面的:作為設計模式的MVC和 作為體系結構模式的MVC。
所謂MVC,指的是一種劃分系統功能的方法,它將一個系統劃分為三個部分:
模型(Model):封裝的是數據源和所有基於對這些數據的操作。在一個組件中,Model往 往表示組件的狀態和操作狀態的方法。
視圖(View):封裝的是對數據源Model的一種顯示。一個模型可以由多個視圖,而一個 視圖理論上也可以同不同的模型關聯起來。
控制器(Control):封裝的是外界作用於模型的操作。通常,這些操作會轉發到模型上 ,並調用模型中相應的一個或者多個方法。一般Controller在Model和View之間起到了溝通的 作用,處理用戶在View上的輸入,並轉發給Model。這樣Model和View兩者之間可以做到松散 耦合,甚至可以彼此不知道對方,而由Controller連接起這兩個部分。
有了前面介紹的諸多模式之後,就可以很容易的通過模式來解釋MVC的內在行為了。前面 說過,在設計模式中,MVC實際上是一個比較高層的模式,它由多個更基本的設計模式組合而 成,Model-View的關系實際上是Observer模式,模型的狀態和試圖的顯示相互響應,而View -Controller則是由Strategy模式所描敘的,View用一個特定的Controller的實例來實現一個 特定的響應策略,更換不同的Controller,可以改變View對用戶輸入的響應。而其它的一些 設計模式也很容易組合到這個體系中。比如,通過Composite模式,可以將多個View嵌套組合 起來;通過FactoryMethod模式來指定View的Controller,等等。
使用MVC的好處,一方面,分離數據和其表示,使得添加或者刪除一個用戶視圖變得很容 易,甚至可以在程序執行時動態的進行。Model和View能夠單獨的開發,增加了程序了可維護 性,可擴展性,並使測試變得更為容易。另一方面,將控制邏輯和表現界面分離,允許程序 能夠在運行時根據工作流,用戶習慣或者模型狀態來動態選擇不同的用戶界面。
Swing號稱是完全按照MVC的思路來進行設計的。在設計開始前,Swing的希望能夠達到的 目標就包括:
模型驅動(Model-Driven)的編程方式。
提供一套單一的API,但是能夠支持多種視感(look-and-feel),為用戶提供不同的界面 。
很自然的可以發現,使用MVC模式能夠有助於實現上面的這兩個目標。
嚴格的說,Swing中的MVC實際上是MVC的一個變體:M-VC。 Swing中只顯示的定義了Model 接口,而在一個UI對象中集成了視圖和控制器的部分機制。View和Control比較松散的交叉組 合在一起,而更多的控制邏輯是在事件監聽者部分引入的。
但是,這並沒有妨礙在Swing中體現MVC的精髓。事實上,在Swing的開發初期,Swing確實 是按照標准的MVC模式來設計的,但是很快的問題就出現了:View和Controller實際上是緊密 耦合的,很難作出一個能夠適應不同View的一般化的Controller來,而且,一般也沒有很大 的必要。
在Swing中基本上每一個組件都會有對應的Model對象。但其並不是一一對應的,一個 Model接口可以為多個Swing對向服務,例如:JProgressBar,JScrollBar,JSlider這三個組 件使用的都是BoundedRangeModel接口。這種模型的共享更能夠從分的體現MVC的內涵。除了 Model接口外,為了實現多個視感間的自由切換,每個Swing組件還包含一個UI接口--也就是 View-Controller,負責對組件的繪制和接受用戶輸入。
Model-View是Subject和Obverser的關系,因而,模型的改變必須要在UI對象中體現出來 。Swing使用了JavaBeans的事件模型來實現這種通知機制。具體而言,有兩種實現辦法,一 是僅僅通知事件監聽者狀態改變了,然後由事件監聽者向模型提取必要的狀態信息。這種機 制對於事件頻繁的組件很有效。另外的一種辦法是模型向監聽者發送包含了已改變的狀態信 息的通知給UI。這兩種方法根據其優劣被分別是現在不同的組件中。比如在JScollBar中使用 的是第一種方法,在JTable中使用的是第二種方法。而對Model而言,為了能夠支持多個View ,它並不知道具體的每一個View。它維護一個對其數據感興趣的Obverser的列表,使得當數 據改變的時候,能夠通知到每一個Swing組件對象。
上面講到的是作為設計模式的MVC。而在J2EE中,Sun更是將MVC提升到了一個體系結構模 式的高度,這兒的MVC的含義就更為廣泛了。與Swing中不同的是,在這兒MVC的各個部件不再 是單純的類或者接口,而是應用程序的一個組成部分!
在J2EE Blueprint中,Sun推薦了一種基於MVC的J2EE程序的模式。對於企業級的分布式應 用程序而言,它更需要支持多種形式的用戶接口。比如,網上商店需要一個HTML的界面來同 網上的客戶打交道,WML的界面可以提供給無線用戶,管理者可能需要傳統的基於Swing的應 用程序來進行管理,而對對商業伙伴,基於XML的Web服務可能對他們更為方便。
MVC無疑是這樣一個問題的有效的解決方法,通過在控制和顯示邏輯分離出核心的數據存 取功能,形成一個Model模塊,能夠讓多種視圖來共享這個Model。
在J2EE中有幾個核心的技術,JSP,JavaBean,Servlet,EJB,SessionBean,EntityBean 構成了J2EE構架的基石。JSP能夠生成HTML,WML甚至XML,它對應於Web應用程序中的View部 分。EJB作為數據庫與應用程序的中介,提供了對數據的封裝。一般EntityBean封裝的是數據 ,SessionBean是封裝的是對數據的操作。這兩個部分合起來,對應於Web應用程序的Model部 分。在技術上,JSP能夠直接對EJB進行存取,但這並不是好辦法,那樣會混淆程序中的顯示 邏輯和控制邏輯,使得JSP的重用性能降低。這時候有兩種解決方法,通過JavaBean或者 Servlet作為中介的控制邏輯,對EJB所封裝的數據進行存取。這時,JavaBean或者Servlet對 應於Web引用程序中的Controller部分。兩種類型的Controller各有其優缺點:JSP同Servlet 的交互不容易規范化,使得交互的過程變得復雜,但是Servlet可以單獨同用戶交互,實際上 JSP的運行時狀態就是Servlet;而由於JavaBean的規范性,JSP同JavaBean的交互很容易,利 用JavaBean的get/set方法,JSP不需要過多的語句就可以完成數據的存取,這能夠讓JSP最大 限度的集中在其視圖功能上,而且,在桌面應用程序中使用JavaBean也很容易,而用Servlet 就相對麻煩許多。根據不同的問題背景,可以選取不同的Controller,有時候也可以兩者混 合使用,或者直接在Servlet中調用JavaBean。
在J2EE中,MVC是一個大的框架,這時我們往往把它不再看作為設計模式,而是作為體系 結構模式的一個應用了。
總結
在這篇文章中,從設計的角度,對Java的類庫進行了一些分析,並著重於設計模式在其中 的使用問題。相信大家看了之後,不論對Java類庫本身,還是設計模式,都應該有了一個更 深層次的了解。當然,Java類庫是一個非常龐大的東西,還有著許多設計精良的結構。因而 ,對Java源代碼的研究,不論對於編碼還是設計,都是很有裨益的。本人接觸設計模式的時 間並不很長,對其的理解可能會有一些偏差,如有謬誤的地方,還請能夠提出,大家能夠共 同的探討。
需要說明的是,對模式的描敘實際上是有一套完整的規格(或者語言)來進行的,涉及到 模式的意圖(Intent),問題描敘(Problem),背景(Context),約束(Force),解決方 案(Solution),結果(Resulting Context)等等。但這兒為了敘述的方便,並沒有將它們 一一列舉。如果需要對模式有詳細系統的研究,就應該對這些規格敘述有更深入的了解。