目錄
摘要 ……………………………………………………………………2
文獻(xiàn)綜述 ………………………………………………………………3
第一章 前言……………………………………………………………6
第二章 OOP的基本原則及發(fā)展方向
第一節(jié) 軟件的可維護(hù)性與可復(fù)用性…………………………6
第二節(jié) 六條OOP設(shè)計(jì)原則 ……………………………………7
第三節(jié) AOP的興起…………………………………………… 8
第三章 J2EE系統(tǒng)的架構(gòu)設(shè)計(jì)
第一節(jié)J2EE中常用的概念……………………………………10
第二節(jié) MVC架構(gòu) ………………………………………………12
第三節(jié) 分布式架構(gòu)……………………………………………13
第四章 數(shù)據(jù)持久層的設(shè)計(jì)
第一節(jié) 業(yè)務(wù)對象的持久化……………………………………14
第二節(jié) 數(shù)據(jù)訪問對象設(shè)計(jì)模式………………………………15
第三節(jié) ORM框架的原理和重要性 ……………………………16
第四節(jié) 數(shù)據(jù)持久層……………………………………………19
第五節(jié) 數(shù)據(jù)庫連接池、緩存及系統(tǒng)性能的提升 ……………21
第六節(jié) Java反射技術(shù) ………………………………………22
第五章 J2EE架構(gòu)中各層的數(shù)據(jù)表示方法
第一節(jié) MVC三層體系結(jié)構(gòu)中的數(shù)據(jù)表示要求 ………………23
第二節(jié) J2EE系統(tǒng)中各層數(shù)據(jù)表示的設(shè)計(jì) …………………24
第六章 設(shè)計(jì)模式的概念與幾種常用的J2EE設(shè)計(jì)模式
第一節(jié) 設(shè)計(jì)模式的概念………………………………………25
第二節(jié) 工廠創(chuàng)建模式與單例模式……………………………27
第三節(jié) 使用工廠模式實(shí)現(xiàn)DAO ………………………………31
總結(jié) ……………………………………………………………………33
結(jié)束語 …………………………………………………………………34
多層J2EE系統(tǒng)的架構(gòu)與模式設(shè)計(jì)
【摘要】 J2EE提供了一套完整的基于標(biāo)準(zhǔn)化模塊的服務(wù)組件,它能自動的處理大多數(shù)應(yīng)用程序的細(xì)節(jié),而不需要復(fù)雜的編程,因此簡化了復(fù)雜的企業(yè)級應(yīng)用程序的開發(fā)。本文首先考察企業(yè)級應(yīng)用的一般概念和需求,然后簡要闡述面向?qū)ο蟪绦蛟O(shè)計(jì)的基本原則,并結(jié)合軟件工程的思想來討論多層的J2EE應(yīng)用架構(gòu),分析它們滿足企業(yè)級應(yīng)用的方式,,再通過講述常用的幾種Java設(shè)計(jì)模式和Java反射技術(shù)來說明如何實(shí)現(xiàn)這些應(yīng)用架構(gòu)。
【關(guān)鍵詞】 模型-視圖-控制,對象關(guān)系映射,業(yè)務(wù)對象,面向方面編程,數(shù)據(jù)訪問對象,設(shè)計(jì)模式
The Framework of Multitier J2EE System and Design Pattern
【abstracts】The J2EE simplifies enterprise applications by basing them on standardized, modular components, by providing a complete set of services to those components, and by handling many details of application behavior automatically,without complex programming. This paper reviews the general concept and the requirement of enterprise application, elaborates the general principle of object oriented programming briefly. We combine the idea of Software-Engineering to discuss the framework of multitier J2EE, and meanwhile analyze how they can satisfy the demand of enterprise applications. At last, this paper shows how to implement those frameworks of multitier J2EE by introducing some kinds of Java design pattern and the Java reflection technology.
【key words】MVC,ORM, BO, AOP, DAO,Design pattern.
【文獻(xiàn)綜述】
計(jì)算機(jī)軟件是人類心靈和智慧在虛擬空間中的投射。軟件的性能是人類能力的擴(kuò)展,它的活動就是人類心智活動的反映。軟件直接表達(dá)出設(shè)計(jì)者對目標(biāo)的理解,對用戶的期待,以及對自己的定位。人們在自己的環(huán)境中不斷發(fā)現(xiàn)問題和尋找問題的解決方案的時(shí)候,發(fā)現(xiàn)有一些問題及其解決方案不斷變換面孔重復(fù)出現(xiàn),但在這些不同的面孔后面有著共同的本質(zhì),這些共同的本質(zhì)就是模式。著名建筑工程學(xué)家Christopher Alexander所著《建筑的永恒之道》( The Timeless Way of Building)和他發(fā)展出來的模式理論涵蓋科學(xué),心理,藝術(shù)和哲學(xué),不僅適用于建筑工程學(xué),而且適用于軟件工程學(xué)以及任何其他的工程學(xué)。
今天的企業(yè)軟件可以由多個(gè)不同的部分組成,但企業(yè)已經(jīng)認(rèn)識到,只要符合企業(yè)利益,很有必要將各個(gè)分散的系統(tǒng)進(jìn)行良好的集成,以盡可能相互支持,總的來說企業(yè)希望對集成后的企業(yè)級軟件的具體應(yīng)用如下:
1. 通過集成企業(yè)的客戶支持和本身的產(chǎn)品知識,企業(yè)可以利用WEB為它的客戶提供更新更好的服務(wù)。
2. 將企業(yè)售貨機(jī)聯(lián)網(wǎng),企業(yè)可以獲得更多的在線客戶。
3. 將銷售管理系統(tǒng)和存貨系統(tǒng)相鏈接,企業(yè)可以設(shè)計(jì)特定的低成本的Web銷售渠道,這樣可以進(jìn)入未曾涉足的市場領(lǐng)域。
4. 如果給企業(yè)員工所使用的服務(wù)提供一個(gè)前端,例如內(nèi)部辦公用品訂貨系統(tǒng),將它與會計(jì)系統(tǒng)連接在一起,企業(yè)就可以降低總體開支并提高員工的工作效率。
5. 在線使用企業(yè)HR系統(tǒng),可以讓員工根據(jù)他們自己的健康狀況進(jìn)行更多的選擇,這樣可以降低企業(yè)整體的管理費(fèi)用。
6. 使企業(yè)的人力資源密集型操作自動化,并使它可用于任何時(shí)間任何地點(diǎn),在降低整體運(yùn)營費(fèi)用的同時(shí),企業(yè)還可以給它的客戶提供更好的服務(wù)。
按企業(yè)對企業(yè)級軟件的要求,一個(gè)企業(yè)級應(yīng)用系統(tǒng)(J2EE)肯定會是一個(gè)服務(wù)于商業(yè)目的,處理企業(yè)業(yè)務(wù)信息,數(shù)據(jù)的軟件系統(tǒng),因此大概可以總結(jié)出以下五方面的特征:有復(fù)雜的業(yè)務(wù)邏輯,有大量持久化數(shù)據(jù),與多種外部系統(tǒng)相關(guān)聯(lián)有較高的性能要求,在運(yùn)行時(shí)需要隨時(shí)監(jiān)控,管理,應(yīng)該能夠?qū)崟r(shí)記錄,觀察系統(tǒng)運(yùn)行情況。修改系統(tǒng)配置。
以前的企業(yè)應(yīng)用,集中式的單層(single tier)應(yīng)用程序占有主導(dǎo)地位。在軟件中,層是一個(gè)抽象概念,它的主要目的是通過將軟件分解成獨(dú)立的邏輯層,幫助我們理解與特定應(yīng)用程序相關(guān)聯(lián)的體系結(jié)構(gòu)。從應(yīng)用程序的角度看,單層應(yīng)用程序的最大問題在于,它將表示,業(yè)務(wù)邏輯和數(shù)據(jù)都混合在一起??蛻魴C(jī)-服務(wù)器方法通過將表示和一些業(yè)務(wù)邏輯分別移至單獨(dú)的層中,緩解了上述主要問題的影響,不過從應(yīng)用程序的角度來看,業(yè)務(wù)邏輯和表示依然很混亂。N層(n-tier)方法可以取得更好的整體平衡,它將表示邏輯與業(yè)務(wù)邏輯從底層數(shù)據(jù)中分離開來,以滿足特定的需求。單單采用面向?qū)ο箝_發(fā)技術(shù)后只可以實(shí)現(xiàn)部分代碼重用,原因之一是對象都細(xì)粒度化,正是因?yàn)榧?xì)粒度對象間更緊密的耦合狀態(tài),從而便利大范圍的重用變得很困難。分層化的組件設(shè)計(jì)就是為了解決這個(gè)問題。與對象不同,軟件組件是在更高的抽象級中設(shè)計(jì)的,可以提供一個(gè)完整的功能或服務(wù)。組件間的耦合更為松散。利用組件的接口,可以將組件迅速組合在一起以構(gòu)建更大的企業(yè)級應(yīng)用程序。
近年來,人們已開發(fā)出了各種不同的幫助理解的組件模型,例如,Microsoft的ActiveX,后來的COM編程接口,和現(xiàn)在興起的.net FrameWork,SUN Microsystems的applet和JavaBeans,Enterprise JavaBeans(EJB),其中EJB是J2EE的一部分。
Sun Microsystems把Java2平臺組織成三個(gè)特定的,引人矚目的版本:微型版(J2ME),標(biāo)準(zhǔn)版(J2SE)和企業(yè)版(J2EE)。在這些產(chǎn)品中,J2EE與開發(fā)企業(yè)級Java應(yīng)用聯(lián)系最緊密。J2EE為開發(fā)復(fù)雜的,分布式企業(yè)級Java應(yīng)用定義了一套體系結(jié)構(gòu)。
J2EE最初是由Sun Microsystems在1999年中期發(fā)布的,其正式發(fā)布則在1999年后期。J2EE仍然較新,其依次發(fā)布的版本間仍然存在著重大的改變,特別是在EJB方面。該平臺是建立在Java“一次編寫,隨意運(yùn)行”的理念上的,它通過一組技術(shù)和一套API實(shí)現(xiàn)。
N層體系結(jié)構(gòu)的概念已經(jīng)出現(xiàn)一段較長的時(shí)間了,并已成功地應(yīng)用于構(gòu)建企業(yè)級應(yīng)用程序。Sun在Java中采用n層開發(fā)模型,并引入特定功能,允許更容易地開發(fā)服務(wù)器端可伸縮的、基于Web的企業(yè)級應(yīng)用程序,從而在這個(gè)領(lǐng)域提供了Java自身所缺少的關(guān)鍵成分。
為什么要使用J2EE呢?它不是太新并且功能未經(jīng)證實(shí),它能提供什么?難道只是一種一時(shí)的技術(shù)狂熱嗎?在J2EE出現(xiàn)之前,JDBC API早已建立好了,可選用的輕量級的,可維護(hù)的servlet技術(shù)也已出現(xiàn)。除了這些,J2EE還提供了一些有前景的優(yōu)點(diǎn),它讓開發(fā)人員關(guān)注開發(fā)業(yè)務(wù)邏輯,不用預(yù)先詳細(xì)了解執(zhí)行環(huán)境而把精力放到實(shí)現(xiàn)系統(tǒng)上,以及創(chuàng)建在硬件平臺和操作系統(tǒng)(OS)間更容易銜接的系統(tǒng)。企業(yè)級軟件開發(fā)是一項(xiàng)復(fù)雜的任務(wù),需要具備許多不同領(lǐng)域的廣泛知識。例如,一項(xiàng)典型的企業(yè)級應(yīng)用程序開發(fā)工作可能要求你熟悉進(jìn)程間的通信問題、安全問題、數(shù)據(jù)庫特定訪問查詢等。
J2EE企業(yè)級開發(fā)平臺鼓勵(lì)在系統(tǒng)開發(fā)、部署和執(zhí)行之間作一個(gè)清晰的劃分。此開發(fā)人員可以將部署細(xì)節(jié)留給部署人員處理,如實(shí)際的數(shù)據(jù)庫名稱和存放位置、主機(jī)持有配置屬性等。J2EE讓系統(tǒng)可通過Java和J2EE而不是底層系統(tǒng)API被訪問,從而支持硬件和OS無關(guān)性。由于這種原因,遵循J2EE體系結(jié)構(gòu)技術(shù)規(guī)范的企業(yè)級系統(tǒng)可以非常容易地在硬件系統(tǒng)和不同的OS之間銜接。
在企業(yè)級開發(fā)領(lǐng)域,雖然面對Microsoft .net強(qiáng)大的挑戰(zhàn),但是J2EE由于上述優(yōu)點(diǎn),并且相對說來比較成熟,已經(jīng)占據(jù)了企業(yè)級開發(fā)的大部分市場,并隨著技術(shù)的進(jìn)步、新的J2EE版本的發(fā)布、開源社區(qū)龐大自由開發(fā)者的支持,將會使企業(yè)級開發(fā)變得更高效,更快速,更高質(zhì)量,更易于維護(hù)。
第一章 前言
J2EE核心技術(shù)有十三種,它們和J2EE API覆蓋了企業(yè)級Java開發(fā)的廣泛領(lǐng)域。在企業(yè)級Java開發(fā)工作中要用到的J2EE的方方面面知識是不太可能的。比較常用的有容器,servlet, JSP, EJB等。容器是一種運(yùn)行在服務(wù)器上的軟件實(shí)體,用于管理特定類型的組件。它為開發(fā)J2EE組件提供了執(zhí)行環(huán)境。通過這些容器,J2EE體系結(jié)構(gòu)就能在開發(fā)和部署間提供無關(guān)性,并在不同類型的中間層服務(wù)器間提供可移植性。servlet是一些可生成動態(tài)內(nèi)容的Web組件。它們是當(dāng)今在www上看到的最常用的J2EE組件之一。它們提供了一種有效的機(jī)制,用于基于服務(wù)器的業(yè)務(wù)邏輯和基于Web的客戶端之間的交互,還可為通用的CGI腳本方法提供一種輕型且更易于管理的替代方法。JSP是另一種類型的J2EE Web組件,它是從servlet技術(shù)發(fā)展而來的。事實(shí)上,一部分JSP編譯進(jìn)servlet并在servlet容器中執(zhí)行。EJB技術(shù)規(guī)范是J2EE平臺的最核心的部分。它為構(gòu)建可伸縮、分布式、基于服務(wù)器的企業(yè)級Java應(yīng)用組件提供了一種綜合性的組件模型。文章將結(jié)合這幾種主要的組件技術(shù)來講述構(gòu)建J2EE系統(tǒng)的一般過程。
第二章 OOP的基本原則及發(fā)展方向
第一節(jié) 軟件的可維護(hù)性與可復(fù)用性
通常認(rèn)為,一個(gè)易于維護(hù)的系統(tǒng),就是復(fù)用率較高的系統(tǒng);而一個(gè)復(fù)用較好的系統(tǒng),就是一個(gè)易于維護(hù)的系統(tǒng)。也就是說一個(gè)系統(tǒng)的設(shè)計(jì)目標(biāo)應(yīng)該具有如下性質(zhì):可擴(kuò)展性,靈活性,可插入性。
常聽人說一個(gè)項(xiàng)目開發(fā)結(jié)束只完了這個(gè)項(xiàng)目的三分之一,可見系統(tǒng)的可維護(hù)的重要性。導(dǎo)致一個(gè)系統(tǒng)可維護(hù)性降低主要有四個(gè)原因:過于僵硬,過于脆弱,復(fù)用率低,黏度過高。通過良好的軟件復(fù)用,可以提高軟件的生產(chǎn)效率,控制生產(chǎn)成本,并能提高軟件的質(zhì)量,改善系統(tǒng)的可維護(hù)性,提高系統(tǒng)的靈活性和可插入性。
在面向?qū)ο蟮脑O(shè)計(jì)里,可維護(hù)性復(fù)用是以設(shè)計(jì)原則和設(shè)計(jì)模式為基礎(chǔ)的,下一節(jié)介紹面向?qū)ο笤O(shè)計(jì)的基本原則。
第二節(jié) 六條OOP設(shè)計(jì)原則
OOP設(shè)計(jì)原則是提高軟件系統(tǒng)的可維護(hù)性和可復(fù)用性的指導(dǎo)性原則,Java是一門純面向?qū)ο蟮脑O(shè)計(jì)語言,因此我們在使用Java開發(fā)J2EE系統(tǒng)時(shí)必須遵守OOP設(shè)計(jì)的基本原則。
這些設(shè)計(jì)原則首先都是復(fù)用的原則,遵循這些設(shè)計(jì)原則可以有效地提高系統(tǒng)的復(fù)用性,同時(shí)提高系統(tǒng)的可維護(hù)性:
l OCP開閉原則:一個(gè)軟件實(shí)體應(yīng)當(dāng)對擴(kuò)展開放,對修改關(guān)閉
l LSP(里氏代換原則):它是繼承復(fù)用的基石
l DIP(依賴倒轉(zhuǎn)原則):要依賴于抽象,不要依賴于具體
l ISP(接口隔離原則):一個(gè)類對另一個(gè)類的依賴性應(yīng)當(dāng)是建立在最小接口上
l CARP(合成/聚合復(fù)用原則):要盡量使用合成/聚合,盡量不要使用繼承
l LOD(迪米特法則):一個(gè)對象應(yīng)該對其他對象有盡可能少的了解
通過擴(kuò)展已有的軟件系統(tǒng),可以提供新的行為,以滿足對軟件的新需求,使變化中的軟件系統(tǒng)有一定的適應(yīng)性和靈活性。而已有的軟件模塊,特別是最重要的抽象層模塊不能再修改,這就使變化莫測中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。具有這些優(yōu)點(diǎn)的軟件系統(tǒng)是一個(gè)在高層次上實(shí)現(xiàn)了復(fù)用的系統(tǒng),也是一個(gè)易于維護(hù)的系統(tǒng)。
里氏代換要求凡是基類型使用的地方,子類型一定適用,因此子類必須具備基類型的全部接口。
傳統(tǒng)的過程性系統(tǒng)的設(shè)計(jì)辦法傾向于使高層次的模塊依賴于低層次的模塊;抽象層次依賴于具體層次。抽象層次包含的是應(yīng)用系統(tǒng)的商務(wù)邏輯和宏觀的,對整個(gè)系統(tǒng)來說是重要的戰(zhàn)略性決定,是必然性的體現(xiàn);而具體層次則含有一些次要的與實(shí)現(xiàn)有關(guān)的算法和邏輯,以及戰(zhàn)術(shù)性的決定,帶有相當(dāng)大的偶然性選擇。具體層次的代碼是會經(jīng)常有變動的,不能避免出現(xiàn)錯(cuò)誤。抽象層次依賴于具體層次,使許多具體層次的細(xì)節(jié)的算法變化立即影響到抽象層次的宏觀商務(wù)邏輯,導(dǎo)致微觀決定宏觀,戰(zhàn)術(shù)決定戰(zhàn)略,偶然決定必然。從哲學(xué)意義上面講這是很荒唐的事情,倒轉(zhuǎn)原則就是要把這個(gè)錯(cuò)誤的依賴關(guān)系倒轉(zhuǎn)過來。
如果兩個(gè)類不必彼此直接通信,那么這兩個(gè)類就不應(yīng)當(dāng)發(fā)生直接的相互作用。如果其中的一個(gè)類需要調(diào)用另一個(gè)類的某一個(gè)方法的話,以可通過第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用。
“針對接口編程”現(xiàn)在已經(jīng)逐漸成為廣大OOP程序員的共識,成為OOP設(shè)計(jì)思想的集中體現(xiàn)。一門語言即使不提供Interface這樣的關(guān)鍵字,它也需要在很大程序上模擬出接口的功能。典型的如C++,通過聲明一個(gè)只有純虛函數(shù)的子類來模擬接口功能。不過這種模擬也就僅限于此,比如對于Java中的動態(tài)代理,抽象基類似乎就無能為力了。
OOP經(jīng)過二十多年的發(fā)展,逐漸取代面向過程程序設(shè)計(jì),已經(jīng)相當(dāng)成熟。OOP的設(shè)計(jì)思想主要體現(xiàn)在下以幾個(gè)方面:
(1) 針對抽象編程,不針對具體編程。這是依賴倒轉(zhuǎn)原則所要求的。換言之,應(yīng)當(dāng)針對抽象類編程,不要針對具體子類編程,這一原則點(diǎn)出了抽象類對代碼利用的一個(gè)最重要的作用。
(2) 使用繼承達(dá)到軟件復(fù)用的目的。在Java中,繼承有兩種,一種是接口繼承,一種是實(shí)現(xiàn)繼承。第二種繼承常常很容易被濫用。只要可能,盡量使用合成,而不要使用繼承來達(dá)到復(fù)用的目的。
(3) 使用模板方式模式,它是類的行為模式,準(zhǔn)備一個(gè)抽象類,將部分邏輯以具體方法以及具體構(gòu)造子的形式實(shí)現(xiàn),然后聲明一些抽象方法來迫使子類實(shí)現(xiàn)剩余邏輯。不同的子類可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對剩余的邏輯有不同的實(shí)現(xiàn)。
第三節(jié) AOP的興起
軟件工程的發(fā)展史實(shí)際上就是抽象的發(fā)展史。隨著軟件越來越復(fù)雜,相應(yīng)地我們也提高了編程語言的方法的抽象級別。因此,我們經(jīng)歷了從C到C++到Java,從結(jié)構(gòu)化方法到面向?qū)ο蟮脑O(shè)計(jì),從類到設(shè)計(jì)模型又到體系結(jié)構(gòu)框架這一系列的改變,并且這個(gè)改變?nèi)匀辉诶^續(xù)著。
AOP(Aspect Oriented Programming) 面向方面編程是一種超越OOP的編程模型,它允許程序員將橫切關(guān)注點(diǎn)(散布在多個(gè)模塊中的一致概念如同步處理,持久化,日志等都是典型的橫切關(guān)注點(diǎn))封裝成清晰的可重用模塊,然后通過組合這些模塊和功能性組件獲得系統(tǒng)的實(shí)現(xiàn)。AOP通過促進(jìn)另一種模塊性補(bǔ)充了OOP,便利我們可以很自然地處理一些傳統(tǒng)的OOP不能解決的問題。
在J2EE應(yīng)用開發(fā)中,我們主要使用AOP的攔截(interception)能力,它提供了在任何對象的方法調(diào)用前/后加入自定義行為的能力。這使得我們可以處理企業(yè)應(yīng)用中的橫切關(guān)注點(diǎn)(同時(shí)作用于多個(gè)對象的關(guān)注點(diǎn)),并且仍然保持強(qiáng)類型,而不需要改變方法簽名。例如,可以在一個(gè)應(yīng)該具有事務(wù)的方法調(diào)用前開始一個(gè)事務(wù),在方法返回時(shí)提交或者回滾。使用AOP可以把與事務(wù)管理相關(guān)的重復(fù)勞動放進(jìn)一個(gè)框架內(nèi)。
作為一個(gè)開發(fā)者,我每天大部分的時(shí)間面對的是數(shù)據(jù)庫的存取,JNDI資源的訪問,事情的聲明釋放,以及各處文件的讀取等等,這些面對不同業(yè)務(wù)的同樣操作。但是,在執(zhí)行一步操作的時(shí)候往往我們需要配置一個(gè)比較完整的環(huán)境,如果JSP/Servlet容器,EJB容器等。在當(dāng)今J2EE主流以發(fā)展輕量級構(gòu)件的時(shí)代,我們有機(jī)會從擺脫這種重復(fù)的勞動,使用上輕量級的構(gòu)件技術(shù)。
輕量級容器依靠反轉(zhuǎn)控制(Inversion of Control. IoC)模式或依賴注入(Dependency Injection)現(xiàn)在是Java社區(qū)非常熱門的話題,它實(shí)際上是針對接口編程這個(gè)OOP概念的進(jìn)一步深化。利用IoC模式能夠很好的解決代碼調(diào)用者和被調(diào)用者之間的依賴關(guān)系,不僅可以靈活的裝配我們的業(yè)務(wù)對象,更重要的是它讓我們有機(jī)會在一個(gè)完整的環(huán)境中進(jìn)行工作,使我們的業(yè)務(wù)開發(fā)不用再考慮環(huán)境因素。如果用得著某幾項(xiàng)J2EE服務(wù),就可以使用他們,比如,想用JTA就用JTA,想用數(shù)據(jù)庫連接池就用連接池。這樣,讓我們的應(yīng)用代碼在J2EE環(huán)境下,不受任何運(yùn)行環(huán)境的束縛。更有特色的是,輕量級容器借助AOP的橫切服務(wù)甚至可以讓我們的代碼段中不包含try/catch這樣的常用代碼成例。因?yàn)檫@樣這種異常處理是與環(huán)境相關(guān)的。
從概念上面來說,JNDI查找,事務(wù),安全之類的基礎(chǔ)設(shè)施都是與業(yè)務(wù)邏輯橫切的。由于業(yè)務(wù)組件都是輕量級容器負(fù)責(zé)管理生命周期,使用者只要是通過容器進(jìn)行組件訪問,我們就應(yīng)該讓容器插入額外的代碼來管理橫切基礎(chǔ)設(shè)施。然而,將這些基礎(chǔ)設(shè)計(jì)進(jìn)行外部聲明,而不讓他們進(jìn)入到應(yīng)用代碼之中,才是一個(gè)系統(tǒng)實(shí)現(xiàn)可插入性的最好的實(shí)現(xiàn)辦法。
在J2EE輕量級容器中,Spring無疑是最流行,發(fā)展得最好的,它提供的IoC容器不僅可以幫助我們完成我們的目標(biāo),而且它是開源的。
那么Spring如果將我們從重復(fù)的勞動中解放出來呢?一般,在我們寫數(shù)據(jù)庫接口時(shí),需要訪問數(shù)據(jù)庫的時(shí)候都需要?jiǎng)?chuàng)建一個(gè)Connection,再聲明一個(gè)statement,然后再進(jìn)行具體的數(shù)據(jù)庫操作,事務(wù)處理完閉之后,還要用try/catch語句將statement,connection close()掉。在JDBC2.0規(guī)范中,我們通常所做的方法是通過容器過(Tomcat5.0,Jboss,Weblogic都提供很好的JNDI支持)定義資源,并暴露為全局性的JNDI對象,這樣,客戶端的代碼將直接通過JNDI訪問這些資源,顯然,應(yīng)用代碼將與JNDI基礎(chǔ)設(shè)施綁定在了一起,也就是說應(yīng)用代碼與環(huán)境相關(guān)了,很難借助偽造的資源對象來進(jìn)行單元測試,而且這些的代碼既不易重用,也不能在J2EE環(huán)境以外運(yùn)行。
第三章 J2EE系統(tǒng)的架構(gòu)設(shè)計(jì)
第一節(jié) J2EE中常用的概念
在軟件業(yè)邁向組件裝配工業(yè)(software component industry) 的過程中﹐不斷發(fā)現(xiàn)組件設(shè)計(jì)者對其組件應(yīng)用場合的預(yù)想環(huán)境與應(yīng)用軟件師的軟件體系結(jié)構(gòu)常常無法很好地整合起來﹐導(dǎo)致應(yīng)用軟件開發(fā)人員難以靈活地復(fù)用他人設(shè)計(jì)好的組件,造成軟件組件工業(yè)發(fā)展上的瓶頸。OOP軟件專家也逐漸認(rèn)識到其問題是來自于軟件主架構(gòu)上的不兼容。軟件主架構(gòu)的重要性并非今天才呈現(xiàn)出來﹐20多年前軟件大師Fred.P.Brooks 就提到:軟件開發(fā)者之間﹐他們設(shè)計(jì)的理念必須一致才能共同創(chuàng)造出簡單易用的軟件,同時(shí)他也強(qiáng)調(diào)軟件主架構(gòu)在達(dá)到概念一致的過程中,合作居于核心角色?,F(xiàn)在,開發(fā)者們在項(xiàng)目開始時(shí)不是討論要不要使用架構(gòu),而是討論究竟該使用什么樣的架構(gòu)。
(1) 體系結(jié)構(gòu)(Architecture)
體系結(jié)構(gòu)也可稱為架構(gòu),所謂軟件架構(gòu)﹐根據(jù)Perry 和Wolfe之定義:Software Architecture = {Elements,F(xiàn)orms, Rationale / Constraint },也就是軟件主架構(gòu) = {組件元素,元素互助合作之模式,基礎(chǔ)要求與限制}。Philippe Kruchten采用上面的定義﹐并說明主架構(gòu)之設(shè)計(jì)就是:將各組件元素以某些理想的合作模式組織起來﹐以達(dá)成系統(tǒng)的基本功能和限制。
(2) 架構(gòu)(Framework)
框架也可稱為應(yīng)用架構(gòu),框架的一般定義就是:在特定領(lǐng)域基于體系結(jié)構(gòu)的可重用的設(shè)計(jì)。也可以認(rèn)為框架是體系結(jié)構(gòu)在特定領(lǐng)域下的應(yīng)用。框架比較出名的例子就是MVC(模型-視圖-控制)。
(3) 庫(Library)
庫應(yīng)該是可重用的、相互協(xié)作的資源的集合,供開發(fā)人員進(jìn)行重復(fù)調(diào)用。它與框架的主要區(qū)別在于運(yùn)行時(shí)與程序的調(diào)用關(guān)系。庫是被程序調(diào)用,而框架則調(diào)用程序。常見的庫有Java API,Apache組織提供的Java開發(fā)包。
(4) 設(shè)計(jì)模式(Design Pattern)
設(shè)計(jì)模式應(yīng)該很熟悉,尤其四人幫所寫的書更是家喻戶曉?!八娜藥汀睂⒛J矫枋鰹椤霸谝欢ǖ沫h(huán)境中解決某一問題的方案”。這三個(gè)事物 — 問題、解決方案和環(huán)境 — 是模式的基本要素。
(5) 平臺(Platform)
由多種系統(tǒng)構(gòu)成,其中也可以包含硬件部分。
在J2EE系統(tǒng)開發(fā)過程中,大致可以分為五大步驟:需求、分析、設(shè)計(jì)、編碼、測試。而體系結(jié)構(gòu)是軟件的骨架,是最重要的基礎(chǔ)。體系結(jié)構(gòu)是涉及到每一步驟中。一般在獲取需要的同時(shí),就應(yīng)該開始分析軟件的體系結(jié)構(gòu)。體系結(jié)構(gòu)現(xiàn)在一般是各個(gè)大的功能模塊組合成,然后描述各個(gè)部分的關(guān)系,J2EE平臺已經(jīng)為我們提供了整個(gè)軟件系統(tǒng)的體系結(jié)構(gòu)。
架構(gòu)是體系結(jié)構(gòu)中每個(gè)模塊中相對細(xì)小的結(jié)構(gòu)。如需要表示W(wǎng)eb技術(shù),就會用到MVC架構(gòu),而Web功能只是整個(gè)軟件體系中的一個(gè)功能模塊。每個(gè)架構(gòu)可以有許多個(gè)實(shí)例,如用Java實(shí)現(xiàn)的MVC架構(gòu)Struts。
而在架構(gòu)之下就是設(shè)計(jì)模式,設(shè)計(jì)模式一般是應(yīng)用于架構(gòu)之中,也可以說是對架構(gòu)的補(bǔ)充。架構(gòu)只是提供了一個(gè)環(huán)境,需要我們填入東西。無論是否應(yīng)用了設(shè)計(jì)模式,都可以實(shí)現(xiàn)軟件的功能,而正確應(yīng)用設(shè)計(jì)模式,是對前人軟件設(shè)計(jì)思想或?qū)崿F(xiàn)方法的一種繼承。
第二節(jié) MVC架構(gòu)
上一節(jié)中提到基于Web開發(fā)的MVC架構(gòu)目前在J2EE的世界內(nèi)空前繁榮。在這些架構(gòu)中,老牌的有Struts、Web work。新興的有Spring MVC、Tapestry、JSF等。這些大多是著名團(tuán)隊(duì)的作品,都提供了較好的層次分隔能力,在實(shí)現(xiàn)良好的MVC 分隔的基礎(chǔ)上,通過提供一些現(xiàn)成的輔助類庫,促進(jìn)了生產(chǎn)效率的提高。
在這么多J2EE架構(gòu)中如何選擇一個(gè)適合自己項(xiàng)目的架構(gòu)呢?什么是衡量一個(gè)架構(gòu)設(shè)計(jì)是否優(yōu)秀的標(biāo)準(zhǔn)?從實(shí)際Web產(chǎn)品研發(fā)的角度而言(而非純粹設(shè)計(jì)上,擴(kuò)展性上,以及支持特性上的比較),目前Struts 也許是第一選擇。它擁有成熟的設(shè)計(jì),同時(shí),也擁有最豐富的信息資源和開發(fā)群體。從較偏向設(shè)計(jì)的角度出發(fā),WebWork2 的設(shè)計(jì)理念更加先進(jìn),其代碼與Servlet API 相分離,這使得單元測試更加便利,同時(shí)系統(tǒng)從B/S結(jié)構(gòu)轉(zhuǎn)向C/S接口也較為簡單。另外,對基于模板的表現(xiàn)層技術(shù)(Velocity、Free Maker和XSLT)的支持,也為程序員提供了除JSP之外的更多的選擇(Struts也支持基于模板的表現(xiàn)層技術(shù),只是實(shí)際中不太常用)。而對于Spring而言,首先,它提供了一個(gè)相當(dāng)靈活和可擴(kuò)展的MVC實(shí)現(xiàn),與WebWork2相比,它在依賴注入方面、AOP 等方面更加優(yōu)秀,但在MVC 框架與底層構(gòu)架的分離上又與WebWork2 存在著一定差距,Spring 的MVC 與Servlet API 相耦合,難于脫離Servlet容器獨(dú)立運(yùn)行,在這點(diǎn)的擴(kuò)展性上,比Webwork2稍遜一籌。Spring對于Web應(yīng)用開發(fā)的支持,并非只限于框架中的MVC部分。即使不使用其中的MVC實(shí)現(xiàn),我們也可以從其他組件,如事務(wù)控制、ORM模板中得益。同時(shí),Spring也為其他框架提供了良好的支持,很容易就可以將Struts與Spring搭配使用。因此,對于Spring在Web應(yīng)用中的作用,應(yīng)該從一個(gè)更全面的角度出發(fā)。
J2EE系統(tǒng)采用三層的MVC架構(gòu)之后,其解決的主要問題無外乎以下幾部分:
(1) 將Web頁面中的輸入元素封裝為一個(gè)(請求)數(shù)據(jù)對象。
(2) 根據(jù)請求的不同,調(diào)度相應(yīng)的邏輯處理單元,并將(請求)數(shù)據(jù)對象作為參數(shù)傳入。
(3) 邏輯處理單元完成運(yùn)算后,返回一個(gè)結(jié)果數(shù)據(jù)對象。
(4) 將結(jié)果數(shù)據(jù)對象中的數(shù)據(jù)與預(yù)先設(shè)計(jì)的表現(xiàn)層相融合并展現(xiàn)給用戶或?qū)⑵涑志没?
這樣的J2EE系統(tǒng)將具有下以幾個(gè)優(yōu)點(diǎn):
(1) 多個(gè)視圖能共享一個(gè)模型。在MVC架構(gòu)中,模型響應(yīng)用戶請求并返回響應(yīng)數(shù)據(jù),視圖負(fù)責(zé)格式化數(shù)據(jù)并把它們呈現(xiàn)給用戶,業(yè)務(wù)邏輯和表示層分離,同一個(gè)模型可以被不同的視圖重用,所以大大提高了代碼的可重用性。
(2) 模型是自包含的,與控制器和視圖保持相對獨(dú)立,所以可以方便地改變應(yīng)用程序的數(shù)據(jù)層和業(yè)務(wù)規(guī)則。由于MVC的三個(gè)模塊相互獨(dú)立,改變其中一個(gè)不會影響其它的兩個(gè),所以依據(jù)這種設(shè)計(jì)思想能構(gòu)造良好的松耦合的構(gòu)件。
(3) 控制器提高了應(yīng)用程序的靈活性和可配置性。
使用MVC需要精心的計(jì)劃,由于它的內(nèi)部原理比較復(fù)雜,所以需要花費(fèi)一些時(shí)間去理解它。將MVC運(yùn)用到J2EE應(yīng)用程序中,會帶來額外的工作量,增加應(yīng)用的復(fù)雜性,所以MVC不適合小型應(yīng)用程序。
面對大量用戶界面,業(yè)務(wù)邏輯復(fù)雜的大型應(yīng)用程序,MVC將會使軟件在健壯性,代碼重用和結(jié)構(gòu)方面上一個(gè)新的臺階,尤其是商業(yè)軟件的高度可變性。在我實(shí)際開發(fā)過的項(xiàng)目(一個(gè)電子商務(wù)網(wǎng)站與一個(gè)搜索引擎,和正在開發(fā)的一個(gè)進(jìn)銷存管理系統(tǒng))中,我都應(yīng)用到了Struts,項(xiàng)目相對來說比較成功,通過實(shí)踐認(rèn)識到并不是系統(tǒng)中加入了Struts的類庫和標(biāo)簽就說明使用了MVC Struts,Struts僅僅是一種思想,你也可以不用它的類庫模擬MVC環(huán)境,軟件的開應(yīng)該要做到靈活多變。
第三節(jié) 分布式架構(gòu)
J2EE的兩大特征就是分層與分布。據(jù)統(tǒng)計(jì),大多數(shù)中小企業(yè)級應(yīng)用都用不上分布式,因此本文側(cè)重介紹多層,簡要的介紹一下分布式的適用范圍。
“記住分布式計(jì)算機(jī)的第一法則:不要分布你的對象!”—(Martin Fowler Patterns of Enterprise Application Architecture)。我們真正需要分布式應(yīng)用嗎?其實(shí)我們早已意識到大部分J2EE應(yīng)用程序,特別是WEB應(yīng)用程序,并不能從分布式體系架構(gòu)中受益。甚至相反,由于前期的過渡設(shè)計(jì),在根本無需分布式的應(yīng)用中大量使用分布式技術(shù),不但沒有享受到分布式的優(yōu)點(diǎn),而且還帶來了不同應(yīng)用層之間昂貴的遠(yuǎn)程調(diào)用,引入了復(fù)雜的遠(yuǎn)程訪問期間基礎(chǔ)架構(gòu)和分布式編程。同時(shí),我們也必須明白邏輯層的分層遠(yuǎn)比物理層的分隔重要。選擇分布式也就是選擇EJB,選擇EJB也就是選擇了重量級組件。
也就是說,一些應(yīng)用選擇分布式架構(gòu)應(yīng)該有足夠的理由。如果真正是明確的業(yè)務(wù)需求,這屬于應(yīng)有的分布式應(yīng)用(internal distribution),你需要根據(jù)特定的情形選擇適當(dāng)?shù)姆植际綑C(jī)制,如果是為了靈活性,在項(xiàng)目的未來某個(gè)時(shí)期也許要遠(yuǎn)程輸出某些功能,應(yīng)用程序的架構(gòu)應(yīng)該是允許引入選擇性的分布式應(yīng)用(selective distribution),而不是在項(xiàng)目早期就進(jìn)行過渡設(shè)計(jì),直接使用分布式架構(gòu)。
第四章 數(shù)據(jù)持久層的設(shè)計(jì)
第一節(jié) 業(yè)務(wù)對象的持久化
業(yè)務(wù)對象(Business Object),是對真實(shí)世界的實(shí)體的軟件抽象,它可以代表業(yè)務(wù)領(lǐng)域中的人,地點(diǎn),事物或概念。業(yè)務(wù)對象包括狀態(tài)和行為,判斷一個(gè)類是否成為業(yè)務(wù)對象的一個(gè)重要標(biāo)準(zhǔn),是看這個(gè)類是否同是擁有狀態(tài)和行為。業(yè)務(wù)對象具有的特征:包含狀態(tài)和行為,代表業(yè)務(wù)領(lǐng)域的人,地點(diǎn),事物或概念??梢灾赜谩Mǔ7譃槿悩I(yè)務(wù)對象:
(1) 實(shí)體業(yè)務(wù)對象,代表人,地點(diǎn),事物或概念的組件,在分布式應(yīng)用中可以作為實(shí)體EJB,在更一般的Web應(yīng)用中可以作為包含狀態(tài)和行為的Java Bean。
(2) 過程業(yè)務(wù)對象,代表應(yīng)用中的業(yè)務(wù)過程或流程,通常依賴于業(yè)務(wù)對象。在J2EE應(yīng)用中,它們通常作為會話EJB或是消息驅(qū)動EJB。在非分布式應(yīng)用中,我們可以使用具有管理和控制應(yīng)用行為的常規(guī)Java Bean。
(3) 事件業(yè)務(wù)對象,代表應(yīng)用中的一些事件,如異常,警告或是超時(shí)。
業(yè)務(wù)對象提供了通用的術(shù)語概念,不管是技術(shù)人員還是非技術(shù)人員都可以共享并理解他們,并且它可以隱藏實(shí)現(xiàn)細(xì)節(jié),對外只暴露接口。
業(yè)務(wù)對象在Java編程中可以分兩類,分別是普通純Java對象(pure old java object or plain ordinary java object or what ever POJO)和持久對象(Persistent Object PO)。
持久對象實(shí)際上必須對應(yīng)數(shù)據(jù)庫中的entity,所以和POJO有所區(qū)別。比如說POJO是由new創(chuàng)建,由GC(垃圾收集器)回收。但是持久對象是insert數(shù)據(jù)庫創(chuàng)建,由數(shù)據(jù)庫delete刪除的。持久對象的生命周期和數(shù)據(jù)庫密切相關(guān)。另外持久對象往往只能存在一個(gè)數(shù)據(jù)庫Connection之中,Connection關(guān)閉以后,持久對象就不存在了,而POJO只要不被GC回收,總是存在的。由于存在諸多差別,因此持久對象PO(Persistent Object)在代碼上肯定和POJO不同,起碼PO相對于POJO會增加一些用來管理數(shù)據(jù)庫entity狀態(tài)的屬性和方法。
持久化意味著通過手工或其化方式輸入到應(yīng)用中的數(shù)據(jù),能夠在應(yīng)用結(jié)束運(yùn)行后依然存在,即使應(yīng)用運(yùn)行結(jié)束或者計(jì)算機(jī)關(guān)閉后,這些信息依然存在,不管什么樣的系統(tǒng)都需要數(shù)據(jù)的持久化。我們將需要持久化處理的BO稱之為PO,當(dāng)應(yīng)用中的業(yè)務(wù)對象在內(nèi)存中創(chuàng)建后,它們不可能永遠(yuǎn)存在,在從內(nèi)存中清除之前,要被持久化到關(guān)系數(shù)據(jù)庫中。
第二節(jié) 數(shù)據(jù)訪問對象設(shè)計(jì)模式
上一節(jié)中提到PO在從內(nèi)存中清除之前要被持久化到關(guān)系數(shù)據(jù)庫中,如何做到這一點(diǎn)呢?OOP是當(dāng)今的主流,但是不得不使用關(guān)系型數(shù)據(jù)庫,因此在企業(yè)級開發(fā)中,對象-關(guān)系的映射(Object-Relation Mapping,簡稱ORM)就是很關(guān)鍵的一部分了。圍繞ORM和數(shù)據(jù)持久化的方式,軟件領(lǐng)域通常采用一種數(shù)據(jù)訪問對象(Data Access Object,簡稱DAO)設(shè)計(jì)模式。DAO模式提供了訪問關(guān)系型數(shù)據(jù)庫系統(tǒng)所需要的所有操作接口,其中包括創(chuàng)建數(shù)據(jù)庫,定義表,字段和索引,建立表間的關(guān)系,更新和查詢數(shù)據(jù)庫等。DAO模式將底層數(shù)據(jù)庫訪問操作與高層業(yè)務(wù)邏輯分離開,對上層提供面向?qū)ο蟮臄?shù)據(jù)訪問接口(接口的實(shí)現(xiàn)通常使用抽象工廠設(shè)計(jì)模式來實(shí)現(xiàn))。在DAO的實(shí)現(xiàn)中,可以采用XML語言來配置對象和關(guān)系型數(shù)據(jù)之間的映射。在映射數(shù)據(jù)庫表時(shí),值對象類及其子類所構(gòu)成的樹形結(jié)構(gòu)被用來映射一個(gè)數(shù)據(jù)庫表,該繼承樹通過XML 配置文件對應(yīng)數(shù)據(jù)庫中的單個(gè)表,這使得底層的關(guān)系型的數(shù)據(jù)庫表結(jié)構(gòu)能夠面向?qū)ο竽P退[藏,另外,由于面向?qū)ο笤O(shè)計(jì)方法中類的可繼承性,采用繼承樹對應(yīng)一個(gè)表的策略使得該映射策略極易擴(kuò)展,并且能夠?qū)⒁粋€(gè)復(fù)雜的數(shù)據(jù)表轉(zhuǎn)化成若干簡單的值對象來表示,提高了系統(tǒng)的可維護(hù)性和可修改性。
對于一般的J2EE應(yīng)用,可以直接通過JDBC編程來訪問數(shù)據(jù)庫。JDBC可以說是訪問持久層最原始,最直接的方法。在企業(yè)級應(yīng)用開發(fā)中,可以通過JDBC編程,來開發(fā)自己的DAO API,將數(shù)據(jù)庫訪問期間操作封裝起來,供業(yè)務(wù)層統(tǒng)一調(diào)用。因此DAO所封裝的接口在團(tuán)隊(duì)開發(fā)中顯得非常的重要,幾乎是項(xiàng)目成敗的關(guān)鍵。
DAO模式在系統(tǒng)中所處的位置
DAO的實(shí)現(xiàn)通常使用工廠方法模式,工廠方法模式將創(chuàng)建實(shí)例的工作與使用實(shí)例的工作分開,也就是說,讓創(chuàng)建實(shí)例所需要的大量初始化工作從簡單的構(gòu)造函數(shù)中分離出去。只需要調(diào)用一個(gè)統(tǒng)一的方法,即可根據(jù)需要?jiǎng)?chuàng)建出各種對象的實(shí)例,對象的創(chuàng)建方法不再用編碼到程序模塊中,而是統(tǒng)一編寫在工廠類中。這樣在系統(tǒng)進(jìn)行擴(kuò)充修改時(shí),系統(tǒng)的變化僅存在于工廠類內(nèi)部,而絕對不會對其他對象造成影響。
第三節(jié) ORM框架的原理和重要性
如果數(shù)據(jù)模型非常復(fù)雜,那么直接通過JDBC編程來實(shí)現(xiàn)持久化框架需要有專業(yè)的知識,這樣可以直接使用采用第三方提供的持久化框架。如Hibernate,Ibatis,JDO。
在使用ORM框架時(shí)也需要遵守OOP設(shè)計(jì)原則和MVC框架的基本原則,都應(yīng)該確保框架沒有滲透到應(yīng)用中,應(yīng)用的上層組件應(yīng)該和ORM框架保持獨(dú)立。ORM追求的目標(biāo)就是要 PO在使用上盡量和POJO一致,對于程序員來說,他們可以把PO當(dāng)作POJO來用,而感覺不到PO的存在。ORM框架就是要做到維護(hù)數(shù)據(jù)庫表記錄的PO完全是一個(gè)符合Java Bean規(guī)范的POJO,沒有增加別的屬性和方法。
同是ORM框架,但不同的ORM工具實(shí)現(xiàn)的方式有所不同,目前主要有兩種ORM工具競爭,JDO與Hibernate,以下介紹一下它們的工作機(jī)理。
JDO的實(shí)現(xiàn)ORM方法如下:
(1) 編寫POJO。
(2) 編譯POJO。
(3) 使用JDO的一個(gè)專門工具,叫做Enhancer,一般是一個(gè)命令行程序,手工運(yùn)行,或者在ant腳本里面運(yùn)行,對POJO的class文件處理一下,把POJO替換成同名的PO。
(4) 在運(yùn)行時(shí)運(yùn)行的實(shí)際上是PO,而不是POJO。
該方法有點(diǎn)類似于JSP,JSP也是在編譯期被轉(zhuǎn)換成Servlet來運(yùn)行的,在運(yùn)行期實(shí)際上運(yùn)行的是Servlet,而不是JSP。
Hibernate的實(shí)現(xiàn)方法如下:
(1) 編寫POJO,通常我們可以通過Hibernate官方提供的MiddleGen for Hibernate 和Hibernate Extension工具包,很方便的根據(jù)現(xiàn)有數(shù)據(jù)庫,導(dǎo)出數(shù)據(jù)庫表結(jié)構(gòu),生成XML格式的ORM配置文件和POJO。
(2) 編譯POJO。
(3) 直接運(yùn)行,在運(yùn)行時(shí),由Hibernate的cglib庫利用Java的反射技術(shù)動態(tài)把POJO轉(zhuǎn)換為PO。
由此可以看出Hibernate是在運(yùn)行時(shí)把POJO的字節(jié)碼轉(zhuǎn)換為PO的,而JDO是在編譯期轉(zhuǎn)換的。一般認(rèn)為JDO的方式效率會稍高,畢竟是編譯期轉(zhuǎn)換嘛。但是Hibernate的作者Gavin King說cglib(Hibernate類庫中的一個(gè)必需使用的jar包)的效率非常之高,運(yùn)行期的PO的字節(jié)碼生成速度非常之快,效率損失幾乎可以忽略不計(jì)。實(shí)際上運(yùn)行時(shí)生成PO的好處非常大,這樣對于程序員來說,是無法接觸到PO的,PO對他們來說完全透明??梢愿幼杂傻囊訮OJO的概念操縱PO。Hibernate查詢語言(Query Language),即HQL,HQL是一種面向?qū)ο蟮牟樵冋Z言,不同于SQL(結(jié)構(gòu)化查詢語言),它具備繼承、多態(tài)和關(guān)聯(lián)等特性。
Hibernate是從PO實(shí)例中取values的,所以即使Session關(guān)閉,也一樣可以get/set,可以進(jìn)行跨Session的狀態(tài)管理。
在N層的J2EE系統(tǒng)中,由于持久層和業(yè)務(wù)層和Web層都是分開的,此時(shí)Hibernate的PO完全可以當(dāng)作一個(gè)POJO來用,在各層間自由傳遞,而不用去管Session是開還是關(guān)。如果你把這個(gè)POJO序列化的話,甚至可以用在分布式環(huán)境中。
因此,在較為常用的數(shù)據(jù)持久方案中,Hibernate的ORM框架是最優(yōu)秀的,下面是對各種持久方案的比較:
l 流行的數(shù)據(jù)持久層架構(gòu):
Business Layer <-> Session Bean <-> Entity Bean <-> DB
l 為了解決性能障礙的替代架構(gòu):
Business Layer <-> DAO <-> JDBC <-> DB
l 使用Hibernate來提高上面架構(gòu)的開發(fā)效率的架構(gòu):
Business Layer <-> DAO <-> Hibernate <-> DB
通過實(shí)際開發(fā)和測試,分析出以上三個(gè)架構(gòu)的優(yōu)缺點(diǎn):
(1) 內(nèi)存消耗:采用JDBC的架構(gòu)無疑是最省內(nèi)存的,Hibernate的架構(gòu)次之,EJB的架構(gòu)最差。
(2) 運(yùn)行效率:如果JDBC的代碼寫的非常優(yōu)化,那么JDBC架構(gòu)運(yùn)行效率最高,但是實(shí)際項(xiàng)目中,這一點(diǎn)幾乎做不到,這需要程序員非常精通JDBC,運(yùn)用Batch語句,調(diào)整PreapredStatement的Batch Size和Fetch Size等參數(shù),以及在必要的情況下采用結(jié)果集cache等等。而一般情況下程序員是做不到這一點(diǎn)的。因此Hibernate架構(gòu)表現(xiàn)出最快的運(yùn)行效率。EJB的架構(gòu)效率會差的很遠(yuǎn)。
(3) 開發(fā)效率:在有Eclipse、JBuilder等開發(fā)工具的支持下,對于簡單的項(xiàng)目,EJB架構(gòu)開發(fā)效率最高,JDBC次之,Hibernate最差。但是在大的項(xiàng)目,特別是持久層關(guān)系映射很復(fù)雜的情況下,Hibernate效率高的驚人,JDBC次之,而EJB架構(gòu)很可能會失敗。
在我工作時(shí)所做過的一些項(xiàng)目的過程中,例如電子商務(wù)網(wǎng)站 (通常是在寫數(shù)據(jù)庫查詢和存儲過程接口時(shí)),面對大量的數(shù)據(jù)反復(fù)存儲調(diào)用,工作量之大無法忍受,這個(gè)很煩惱的持久層開發(fā)的問題一直在困擾我。持久層的開發(fā)現(xiàn)在一般來說要么用CMP,要么用JDBC+DAO。 CMP需要的成本太高,對于這種無需對象分布的系統(tǒng)并不實(shí)用,而JDBC+DAO也存在很多的困難,尤其是數(shù)據(jù)庫表很龐大關(guān)系很復(fù)雜(通常的商業(yè)軟件的數(shù)據(jù)庫都非常的龐大)的時(shí)候,我很難做到把關(guān)系表記錄完整的映射到PO的關(guān)系上來,這主要體現(xiàn)在多表的關(guān)系無法直接映射到對持久對象的映射上來,可能是一個(gè)表映射多個(gè)持久對象,有可能是多個(gè)表映射一個(gè)PO,更有可能的是表的某些字段映射到一個(gè)持久對象,但是另外一些字段映射到別的持久對象上。而且即使這些問題都處理好了,也不能直接按照對象的方式來對持久對象(PO)編程,因?yàn)榇嬖?FONT face="Times New Roman">1:N關(guān)系的持久對象的查詢其實(shí)就是1+n次對數(shù)據(jù)庫的SQL,等于是完全拋棄了對象設(shè)計(jì),完全是按照表字段進(jìn)行操作,但在這種情況下重復(fù)編程量簡直無法想象。更重要的是這樣做會為系統(tǒng)的成敗留下非常大的隱患,因?yàn)橐话阆到y(tǒng)是從需求設(shè)計(jì),系統(tǒng)設(shè)計(jì)這樣自頂而下的,如果都到了詳細(xì)設(shè)計(jì)階段被持久層映射問題限制,不得不自底向上修改設(shè)計(jì)方案,又回到了過程化設(shè)計(jì)上來,嚴(yán)重了違背了依賴倒轉(zhuǎn)原則(DIP)。 很明顯我并不是第一個(gè)遇到這種問題的人,其實(shí)這是一個(gè)經(jīng)典的問題:對象和關(guān)系的映射問題。自從OOP流行以來,就一直存在這個(gè)難題,所以才有人提出將關(guān)系數(shù)據(jù)庫進(jìn)行重新設(shè)計(jì),也會有對象型數(shù)據(jù)庫的出現(xiàn),但實(shí)際上關(guān)系數(shù)據(jù)庫并沒有被淘汰,于是就只能在上層的應(yīng)用層找解決方案。ORM產(chǎn)品正是為這種解決方案而設(shè)計(jì)的。 一個(gè)好的ORM應(yīng)該有如下的特點(diǎn):
(1) 開源和免費(fèi)的License,任何人都可以在需要的時(shí)候研究源代碼,改寫源代碼,進(jìn)行功能的定制。
(2) 輕量級封裝,避免引入過多復(fù)雜的問題,調(diào)試容易,也減輕程序員的負(fù)擔(dān)。
(3) 具有可擴(kuò)展性,API開放,當(dāng)功能不夠用的時(shí)候,用戶可以自行編碼進(jìn)行擴(kuò)展。
(4) 開發(fā)者活躍,產(chǎn)品有穩(wěn)定的發(fā)展保障。
Hibernate符合以上ORM的標(biāo)準(zhǔn),它的文檔也是非常有特色的地方(而且提供了簡體中文版),它不僅僅是 Hibernate的功能介紹那么簡單,它實(shí)際上是一個(gè)持久層設(shè)計(jì)的最佳實(shí)踐的經(jīng)驗(yàn)總結(jié),文檔里面的例子和總結(jié)全部都是最佳設(shè)計(jì)的結(jié)晶。SUN公司最近發(fā)布的EJB3.0中的實(shí)體EJB仿效了Hibernate這種輕量級組件技術(shù)。
第四節(jié) 數(shù)據(jù)持久層
數(shù)據(jù)持久化問題關(guān)鍵在于它的復(fù)雜性。 復(fù)雜性是應(yīng)用開發(fā)過程中最令人頭疼的一個(gè)問題。每當(dāng)在一個(gè)應(yīng)用中增加一個(gè)功能時(shí),它的復(fù)雜性通常呈幾何級的增長。這種復(fù)雜性往往導(dǎo)致程序的開發(fā)無法再繼續(xù)下去。
專家將應(yīng)用開發(fā)過程產(chǎn)生的復(fù)雜性分為兩類,即非本質(zhì)的(accidental) 和本質(zhì)的(essential)。本質(zhì)的復(fù)雜性是對于解決目標(biāo)問題所必然產(chǎn)生的復(fù)雜性,非本質(zhì)的復(fù)雜性是由于選擇了不適當(dāng)?shù)拈_發(fā)工具和設(shè)計(jì)工具而產(chǎn)生的復(fù)雜性。對于一個(gè)功能確定的程序來講,本質(zhì)的復(fù)雜性是確定的,而非本質(zhì)的復(fù)雜性則是沒有限制的。因此,一個(gè)應(yīng)用的開發(fā)要想較順利地取得成功,就需要盡可能地減少非本質(zhì)的復(fù)雜性。
設(shè)計(jì)模式使人們可以更加簡單方便地復(fù)用成功的設(shè)計(jì)和體系結(jié)構(gòu)。將已證實(shí)的技術(shù)表述成設(shè)計(jì)模式,也會使新系統(tǒng)開發(fā)者更加容易理解其設(shè)計(jì)思路。
衡量一個(gè)系統(tǒng)優(yōu)秀與否的關(guān)鍵因素,除了能夠滿足用戶需求外還有如下方面:首先是靈活性。靈活性意指這種結(jié)構(gòu)或模式不依賴于任何實(shí)際應(yīng)用,應(yīng)該與操作系統(tǒng)、應(yīng)用程序無關(guān)。提供獨(dú)立的結(jié)構(gòu),可以提供最大的重用。其次是可擴(kuò)展性。隨著業(yè)務(wù)的擴(kuò)展,新的業(yè)務(wù)不斷增加,業(yè)務(wù)邏輯自然增加,系統(tǒng)必然會進(jìn)行修改或添加相應(yīng) 功能模塊。再次是可配置性。最后是安全性。
數(shù)據(jù)持久層的設(shè)計(jì)采納了多種設(shè)計(jì)模式,最大限度的降低了系統(tǒng)內(nèi)部各模塊、子系統(tǒng)間的耦合性,使得系統(tǒng)相對易于擴(kuò)展,并且能夠在進(jìn)行改變時(shí),保證持久層的業(yè)務(wù)邏輯層相對穩(wěn)定,基本不需要因持久層的調(diào)整改變而進(jìn)行邏輯層的變動。
根據(jù)數(shù)據(jù)源不同,數(shù)據(jù)訪問也不同。根據(jù)存儲的類型(關(guān) 系數(shù)據(jù)庫、面向?qū)ο髷?shù)據(jù)庫等)和供應(yīng)商不同,持久性存儲(比如數(shù)據(jù)庫)的訪問差別也很大。當(dāng)業(yè)務(wù)組件或表示組件需要訪問某數(shù)據(jù)源時(shí),它們可以使用合適的 API來獲得連接性,以及操作該數(shù)據(jù)源。但是在這些組件中包含連接性和數(shù)據(jù)訪問代碼會引入這些組件及數(shù)據(jù)源實(shí)現(xiàn)之間的緊密耦合。組件中這類代碼依賴性使應(yīng)用程序從某種數(shù)據(jù)源遷移到其它種類的數(shù)據(jù)源將變得非常麻煩和困難,當(dāng)數(shù)據(jù)源變化時(shí),組件也需要改變,以便于能夠處理新類型的數(shù)據(jù)源。
數(shù)據(jù)持久層通過調(diào)整抽象工廠(Abstract Factory)模式和工廠方法(Factory Method) 模式,使DAO模式達(dá)到了很高的靈活度。 數(shù)據(jù)持久層使用數(shù)據(jù)訪問對象來抽象和封裝所有對數(shù)據(jù)源的訪問。DAO管理著與數(shù)據(jù)源的連接以便于檢索和存儲數(shù)據(jù),DAO實(shí)現(xiàn)了用來操作數(shù)據(jù)源的訪問機(jī)制,內(nèi)部封裝了對 Hibernate數(shù)據(jù)操縱、事務(wù)處理、會話管理等API的封裝。外界依賴于DAO的業(yè)務(wù)組件為其客戶端使用DAO提供了更簡單的接口,DAO完全向 客戶端隱藏了數(shù)據(jù)源實(shí)現(xiàn)細(xì)節(jié)。由于當(dāng)?shù)蛯訑?shù)據(jù)源實(shí)現(xiàn)變化時(shí),DAO向客戶端提供的接口不會變化,采用該設(shè)計(jì)模式允許DAO調(diào)整到不同的存儲模式,而不會影 響其客戶端或業(yè)務(wù)組件,即使將來不再采用Hibernate作為關(guān)系映射框架,上層客戶端也不會受到任何影響。另外,DAO還充當(dāng)組件和數(shù)據(jù)源之間的適配 器的角色。
當(dāng)?shù)讓哟鎯﹄S著實(shí)現(xiàn)的變化而變化時(shí),該策略可以通過使用抽象工廠模式實(shí)現(xiàn)。抽象工廠可以基于工廠方法實(shí)現(xiàn)而創(chuàng)建,并可使用工廠方法實(shí)現(xiàn)。該策略提供一個(gè)DAO的抽象工廠對象,其中該對象可以構(gòu)造多種類型的具體的DAO工廠,每個(gè)工廠支持一種不同類型的持久性存儲實(shí)現(xiàn)。一旦你獲取某特定實(shí)現(xiàn)的具體DAO工廠,可以使用它來生成該實(shí)現(xiàn)中所支持和實(shí)現(xiàn)的DAO。
第五節(jié) 數(shù)據(jù)庫連接池、緩存及系統(tǒng)性能的提升
通過使用框架和設(shè)計(jì)模式對數(shù)據(jù)訪問的封裝,極大的提高了軟件的可擴(kuò)展可維護(hù)性,同時(shí)也帶來了系統(tǒng)的性能問題,J2EE系統(tǒng)通過以下一些技術(shù)解決這些問題。
緩存(Cache),對于數(shù)據(jù)庫來說,廠商的做法往往是在內(nèi)存中開辟相應(yīng)的區(qū)域來存儲可能被多次存取的數(shù)據(jù)和可能被多次執(zhí)行的語句,以使這些數(shù)據(jù)在下次被訪問時(shí)不必再次提交對DBMS的請求和那些語句在下次執(zhí)行時(shí)不必再次編譯。同樣,數(shù)據(jù)持久層采用緩存技術(shù)來保存已經(jīng)從數(shù)據(jù)庫中檢索出來的部分常用數(shù)據(jù)??蛻舳嗽L問持久層時(shí),持久層將首先訪問緩存,如果能夠命中則直接從緩存中提取數(shù)據(jù),否則再向數(shù)據(jù)庫發(fā)送提取數(shù)據(jù)的指令。這種設(shè)計(jì)能夠大幅度地提高數(shù)據(jù)訪問速度。
數(shù)據(jù)庫連接池(Connection Pool),池是一個(gè)很普遍的概念,和緩沖存儲有機(jī)制相近的地方,都是縮減了訪問的環(huán)節(jié),但它更注重于資源的共享。對于訪問數(shù)據(jù)庫來說,建立連接的代價(jià)比較昂貴,因此,數(shù)據(jù)持久層建立了“連接池”以提高訪問的性能。數(shù)據(jù)持久層把連接當(dāng)作對象,整個(gè)系統(tǒng)啟動后,連接池首先建立若干連接,訪問本來需要與數(shù)據(jù)庫連接的區(qū)域,都改為和池相連,池臨時(shí)分配連接供訪問使用,結(jié)果返回后,訪問將連接交還。這種設(shè)計(jì)消除了JDBC與數(shù)據(jù)源建立連接的延時(shí),同時(shí)在應(yīng)用級提供了對數(shù)據(jù)源的并發(fā)訪問。
享元模式(Flyweight),面向?qū)ο笳Z言的原則就是一切都是對象,但是如果真正使用起來,有時(shí)對象數(shù)可能顯得很龐大,比如,數(shù)據(jù)庫中的記錄,如果以每條記錄作為一個(gè)對象,提取幾千條記錄,對象數(shù)就是幾千,這無疑相當(dāng)耗費(fèi)內(nèi)存。數(shù)據(jù)持久層依據(jù)享元模式設(shè)計(jì)了若干元類,封裝可以被共享的類。這種設(shè)計(jì)策略顯著降低了系統(tǒng)的內(nèi)存消耗。
第六節(jié) Java反射技術(shù)
在不了解的情況下要想操縱一個(gè)Java對象,需要使用Java的反射技術(shù)。反射技術(shù)的出現(xiàn)為Java開發(fā)工具和服務(wù)容器項(xiàng)目的開發(fā)提供了新的技術(shù)手段,例如第四章中提到的Hibernate在運(yùn)行時(shí)使用cglib類利用Java反射技術(shù)動態(tài)地將POJO轉(zhuǎn)化為PO。
Java的J2SE開發(fā)包中包含一個(gè)特殊的部分:Java反射接口,被放置在java.lang.reflect包中。反射使Java的類,包和接口實(shí)現(xiàn)了自我描述和動態(tài)操作的功能,這些功能對于Java應(yīng)用程序的開發(fā)者來說,似乎過于基礎(chǔ)化了,但是反射提供的不僅僅是是一項(xiàng)功能那么簡單,通過反射,Java提供了新的一種非直接參與調(diào)用的方式,打破了一些固有的限制。反射已經(jīng)為多個(gè)成功的項(xiàng)目所運(yùn)用,產(chǎn)生了出乎意料的成功效果。配合一些把基礎(chǔ)的反射功能擴(kuò)展的工具項(xiàng)目,Java的動態(tài)功能得以出色地發(fā)揮出來。Hibernate也得益于反射,反射使Hibernate的代碼更簡潔有效,在操作時(shí)增強(qiáng)了功能。
反射技術(shù)機(jī)制使很多原來困難的任務(wù)變得容易實(shí)現(xiàn)了,反射技術(shù)的功能很強(qiáng)大,但是開發(fā)者在實(shí)際的開發(fā)過程中,并不一定非要使用反射技術(shù)不可,需要根據(jù)具體的情形來選擇技術(shù)方案。即使一定要使用反射技術(shù),也需要一些已有的工具的支持。
第五章 J2EE架構(gòu)中各層的數(shù)據(jù)表示
第一節(jié)MVC三層體系結(jié)構(gòu)中的數(shù)據(jù)表示要求
三層體系結(jié)構(gòu)中的數(shù)據(jù)表示首先要做到的是數(shù)據(jù)的低耦合度,以Struts三層體系結(jié)構(gòu)為例,三層數(shù)據(jù)的表示應(yīng)該如下:
- Web層的數(shù)據(jù)表示是ActionFormBean,數(shù)據(jù)來源于html Form post
- 業(yè)務(wù)層的數(shù)據(jù)表示是VO
- 持久層的數(shù)據(jù)表示是PO,其數(shù)據(jù)來源于數(shù)據(jù)庫,持久層的數(shù)據(jù)表示例如CMP
按照MVC的設(shè)計(jì)原則,層與層之間應(yīng)該保持相對獨(dú)立,數(shù)據(jù)的傳遞在不同的層之間通常利用Java Bean來創(chuàng)建數(shù)據(jù)傳輸對象(Data Transfer Object,簡稱DTO),從技術(shù)的角度上面來說,采用DTO來傳輸數(shù)據(jù)可以減少傳輸數(shù)據(jù)的冗余,提高傳輸效率,更重要的是實(shí)現(xiàn)了各個(gè)層之間的獨(dú)立,使每個(gè)層分工明確。模型層負(fù)責(zé)業(yè)務(wù)邏輯,視圖層負(fù)責(zé)向用戶展示模型狀態(tài)。采用DTO,模型層對視圖層屏蔽了業(yè)務(wù)邏輯細(xì)節(jié),向視圖層提供可以直接顯示給用戶的數(shù)據(jù)。在一個(gè)規(guī)范的J2EE架構(gòu)中,不同層的數(shù)據(jù)表示應(yīng)該被限制在層內(nèi),而不應(yīng)該擴(kuò)散到其它層,這樣可以降低層間的耦合性,提高J2EE架構(gòu)整體的可維護(hù)性和可擴(kuò)展性。比如說Web層的邏輯進(jìn)行了修改,那么只需要修改Web層的Form Bean結(jié)構(gòu),而不需要觸動業(yè)務(wù)層和持久層的代碼修改。同樣的,當(dāng)數(shù)據(jù)庫表進(jìn)行了小的調(diào)整,那么也只需要修改持久層數(shù)據(jù)表示,而不需要觸動業(yè)務(wù)層代碼和Web層代碼。
先來談?wù)?FONT face="Times New Roman">ActionFormBean和持久層的PO之間的重大區(qū)別:在簡單的應(yīng)用中,ActionFormBean和PO幾乎是沒有區(qū)別,所以很多人干脆就是用ActionFormBean來充當(dāng)PO,于是 ActionFormBean從JSP頁面到Servlet控制層再到業(yè)務(wù)層,然后穿過持久層,最后一直映射到數(shù)據(jù)庫表。系統(tǒng)以后需要修改其工作量無法想像。但是在復(fù)雜的應(yīng)用中,ActionFormBean和PO是分離的,它們也不可能一樣。ActionFormBean是和網(wǎng)頁里面的Form表單一一對應(yīng)的,F(xiàn)orm里面有什么元素,Bean里面就有什么屬性。而PO和數(shù)據(jù)庫表對應(yīng),因此如果數(shù)據(jù)庫表不修改,那么PO也不會修改,如果頁面的流程和數(shù)據(jù)庫表字段對應(yīng)關(guān)系不一致,那么又如何能夠使用ActionFormBean來取代PO呢?例如一個(gè)用戶注冊頁面要求注冊用戶的基本信息,因此HTML Form里面包含了基本信息屬性,于是你需要一個(gè)ActionFormBean來一一對應(yīng),每個(gè)Bean屬性對應(yīng)一個(gè)文本框或者選擇框等。而用戶這個(gè)持久對象的屬性和ActionFormBean有什么不同呢?它會有一些ActionFormBean所沒有的集合屬性,如用戶的權(quán)限屬性,用戶的組屬性,用戶的帖子等等。另外還有可能的是在ActionFormBean里面有3個(gè)屬性,分別是用戶的First Name, Middle Name, Last Name,或者干脆在User這個(gè)持久對象中就是一個(gè) Name 對象屬性。假設(shè)注冊頁面原來只要提供First Name,那么ActionFormBean就這一個(gè)屬性,后來要提供全名,要改ActionFormBean,加兩個(gè)屬性。但是這個(gè)時(shí)候PO是不應(yīng)該修改,因?yàn)閿?shù)據(jù)庫沒有改。
第二節(jié) J2EE系統(tǒng)中各層數(shù)據(jù)表示的設(shè)計(jì)
在一個(gè)完整的N層J2EE系統(tǒng)中應(yīng)該如何進(jìn)行合理數(shù)據(jù)表示設(shè)計(jì)呢?Struts是這樣做的:
JSP(View) ---> Action Form Bean (Module) ---> Action(Control)Action
Form Bean是Web層的數(shù)據(jù)表示,它和HTML頁面Form對應(yīng),只要Web頁面的操作流程發(fā)生改變,它就要相應(yīng)的進(jìn)行修改,它不應(yīng)該也不能被傳遞到業(yè)務(wù)層和持久層,否則一旦頁面修改,會一直牽連到業(yè)務(wù)層和持久層的大面積的代碼進(jìn)行修改,Action就是它的邊界。
Action(Web Control) ---> Business Bean ---> DAO ---> ORM --->DB
PO則是業(yè)務(wù)層和持久層的數(shù)據(jù)表示,它在業(yè)務(wù)層和持久層之間進(jìn)行流動,它不應(yīng)該也不能被傳遞到Web層的View中去,而ActionServlet就是它的邊界。
再來看看整個(gè)架構(gòu)的數(shù)據(jù)流程:當(dāng)用戶通過瀏覽器訪問網(wǎng)頁,提交了一個(gè)頁面。于是Action得到了這個(gè)Form Bean,它會把Form Bean屬性讀出來,然后構(gòu)造一個(gè)PO對象,再調(diào)用業(yè)務(wù)層的Bean類,完成了注冊操作,重定向到成功頁面。而業(yè)務(wù)層Bean收到這個(gè)PO對象之后,調(diào)用DAO接口方法,進(jìn)行持久對象的持久化操作。當(dāng)用戶查詢某個(gè)會員的信息的時(shí)候,他用全名進(jìn)行查詢,于是Action得到一個(gè)UserNameFormBean包括了3個(gè)屬性,分別是first name, middle name, last name,然后Action把UserNameFormBean的3個(gè)屬性讀出來,構(gòu)造Name對象,再調(diào)用BO,把Name對象傳遞給BO,進(jìn)行查詢。BO取得Name(注意: Name對象只是User的一個(gè)屬性)對象之后調(diào)用DAO接口,返回一個(gè)User的PO對象,注意這個(gè)User不同于在Web層使用的UserFormBean。然后BO把User對象返回給Action。Action得到User對象之后,把User的基本屬性取出,構(gòu)造UserFormBean,然后把UserFormBean request.setAttribute(…),然后重定向到查詢結(jié)果頁面。查詢頁面拿到request對象里面的ActionFormBean,自動調(diào)用tag顯示。
第六章 設(shè)計(jì)模式的概念與幾種常用的J2EE設(shè)計(jì)模式
第一節(jié) 設(shè)計(jì)模式的概念
程序設(shè)計(jì)是思維具體化的一種方式,是思考如何解決問題的過程,設(shè)計(jì)模式是在解決問題的過程中,一些良好思路的經(jīng)驗(yàn)集成。最早提到設(shè)計(jì)模式,人們總會提到Gof的著作,它最早將經(jīng)典的23種模式集合在一起說明,對后期學(xué)習(xí)程序設(shè)計(jì),尤其是對從事物件導(dǎo)向程序的人們起了莫大的影響。對于Java設(shè)計(jì)師來說,源代碼的匯聚按尺度來分,可以分為由Java語句組成的“代碼模式”,由Java類和對象組成的“設(shè)計(jì)模式”,由大尺度的構(gòu)件組成的“架構(gòu)模式”。
Gof 提到的OOP中設(shè)計(jì)模式涵蓋三大類。
(1) 創(chuàng)建Creational 模式
對象的產(chǎn)生需要消耗系統(tǒng)資源,所以如何有效率的產(chǎn)生、管理與操作對象,一直都是值得討論的課題, Creational 模式即與對象的建立相關(guān),在這個(gè)分類下的模式給出了一些指導(dǎo)原則及設(shè)計(jì)的方向。
l 簡單工廠(Simple Factory) 模式
l 抽象工廠(Abstract Factory) 模式
l 建造(Builder) 模式
l 工廠方法(Factory Method) 模式
l 原始模型(Prototype) 模式
l 單例(Singleton) 模式
l 多例(Multition)模式
(2) 結(jié)構(gòu)Structural 模式
如何設(shè)計(jì)對象之間的靜態(tài)結(jié)構(gòu),如何完成物件之間的繼承、實(shí)現(xiàn)與依賴關(guān)系,這關(guān)系到系統(tǒng)設(shè)計(jì)出來是否健壯(robust):是否易懂、易維護(hù)、易修改、耦合度低等。Structural 模式正如其名,其分類下的模式給出了在不同場合下所適用的各種對象關(guān)系結(jié)構(gòu)。
l 缺省適配(Default Adapter) 模式
l 適配(Adapter)模式
l 橋梁(Bridge) 模式
l 合成(Composite) 模式
l 裝飾(Decorator) 模式
l 門面(Fa?ade) 模式
l 享元(Flyweight) 模式
l 代理(Proxy) 模式
(3) 行為Behavioral 模式
對象之間的合作行為構(gòu)成了程序的最終行為,對象之間若有良好的行為互動,不僅使得程序執(zhí)行時(shí)更有效率,更可以讓對象的職責(zé)更為清晰、整個(gè)程序的動態(tài)結(jié)構(gòu)(對象之間的互相調(diào)用)更有彈性。
l 責(zé)任鏈(Chain of Responsibility) 模式
l 命令(Command) 模式
l 解釋器(Interpreter) 模式
l 迭代子(Iterator) 模式
l 調(diào)停者(Mediator) 模式
l 備忘錄(Memento) 模式
l 觀察者(Observer) 模式
l 狀態(tài)(State) 模式
l 策略(Strategy) 模式
l 模版方法(Template Method) 模式
l 訪問者(Visitor) 模式
第二節(jié) 工廠創(chuàng)建模式與單例模式
l 簡單工廠(Simple Factory)模式:又稱靜態(tài)工廠方法模式(Static Factory Method Pattern)
l 工廠方法(Factory Method)模式:又稱多態(tài)性工廠(Polymorphic Factory)模式或虛擬構(gòu)造子(Virtual Constructor)模式
l 抽象工廠(Abstract Factory)模式:又稱工具箱(Toolkit)模式
下面以簡單工廠設(shè)計(jì)模式為例說明代碼模式在Java編程中的應(yīng)用。
從上圖可以看出,簡單工廠模式涉及到工廠角色,抽象產(chǎn)品角色以及具體產(chǎn)品角色等三個(gè)角色:
l 工廠類(Creator)角色:擔(dān)任這個(gè)角色的是工廠方法模式的核心,含有與應(yīng)用緊密相關(guān)的商業(yè)邏輯,工廠類在客戶端的直接調(diào)用下創(chuàng)建產(chǎn)品對象,它往往由一個(gè)具體Java類來實(shí)現(xiàn)
l 抽象產(chǎn)品(Product)角色:擔(dān)任這個(gè)角色的類是由工廠方法模式所創(chuàng)建的對象的父類,或它們共同擁有接口。抽象產(chǎn)品角色可以用一個(gè)Java接口或者Java抽象類實(shí)現(xiàn)
l 具體產(chǎn)品(Concrete Product)角色:工廠方法模式所創(chuàng)建的任何對象都是這個(gè)角色的實(shí)例,具體產(chǎn)品角色由一個(gè)具體Java類實(shí)現(xiàn)
例如我們要在程序中產(chǎn)生兩個(gè)對象:一個(gè)圓形與一個(gè)方形,建立時(shí)要同時(shí)設(shè)定它們的中心位置,然后它們會負(fù)責(zé)畫出自己。我們可以設(shè)計(jì)一個(gè) Shape Factory工廠類,負(fù)責(zé)創(chuàng)建指定的對象,調(diào)用者只管指定對象名稱與中心位置,而不管這些對象如何產(chǎn)生,對象生成后的中心位置設(shè)定被隱藏于 Shape Factory工廠類中。
如上圖所示的,Main代表了客戶的角色,它只依賴于表層業(yè)務(wù)調(diào)用,而不關(guān)心特定的實(shí)例,實(shí)例的細(xì)節(jié)由Shape Factory完成,我們以一個(gè)簡單的程序來實(shí)現(xiàn)上面這個(gè)UML類圖:
public interface IShape { //建立IShape接口
public void setCenter(int x, int y);
public void draw(); //定義一個(gè)接口方法
}
- Circle類實(shí)現(xiàn)IShape接口
public class Circle implements IShape {
private int x;
private int y;
public void setCenter(int x, int y) {//實(shí)現(xiàn)IShape接口setCenter方法
this.x = x;
this.y = y;
}
public void draw() { //實(shí)現(xiàn)IShape接口draw()方法
System.out.println("Circle center at ("
+ x + ", " + y + ")");
}
}
- Square類實(shí)現(xiàn)IShape接口
public class Square implements IShape {
private int x;
private int y;
public void setCenter(int x, int y) { //實(shí)現(xiàn)IShape接口setCenter方法
this.x = x;
this.y = y;
}
public void draw() {//實(shí)現(xiàn)IShape接口draw()方法
System.out.println("Square center at ("
+ x + ", " + y + ")");
}
}
public class ShapeFactory {
public static IShape createShapeAt(String name, int x, int y) {
try {
IShape shape
= (IShape) Clas.forName(name).newInstance();//使用Java反射技術(shù)獲得名為name產(chǎn)品實(shí)例,并讓IShape接口持有此實(shí)例對象
shape.setCenter(x,y);
return shape;
}
catch(Exception e) {
System.out.println(
"Sorry! No such class defined!"); return null;
}
}
}
public class Main {
public static void main(String args[]) {
// 產(chǎn)生一個(gè)圓形并且顯示它
ShapeFactory.createShapeAt("Circle", 10, 10).draw();
System.Out.println();
// 產(chǎn)生一個(gè)方形并顯示它
ShapeFactory.createShapeAt("Square", 20, 25).draw();
}
}
客戶只要面對Factory,客戶依賴于產(chǎn)品的調(diào)用介面,產(chǎn)品的具體實(shí)例是可以與客戶隔開的,它們也是可以互換的。簡單工廠模式是工廠方法模式與抽象工廠模式的一種特殊情況,關(guān)于它們的實(shí)現(xiàn)可以參考相關(guān)資料。
單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。單例模式在系統(tǒng)設(shè)計(jì)時(shí)使用非常廣泛,常用的資源管理器一般使用此模式,在一些線程安全的情況下使用也比較多,如數(shù)據(jù)庫連接池訪問接口,屬性文件管理等。
以上面的UML圖為例來說明單例模式的使用方法,我們可以在第一次需要實(shí)例時(shí)再創(chuàng)建對象,也就是采用所謂的Lazy Initialization懶漢式:
public class Singleton {
private static Singleton instance = null;
private Singleton() {
// .......
}
public static Singleton getInstance() { //獲得系統(tǒng)唯一實(shí)例的靜態(tài)方法
if (instance == null) instance = new Singleton();
return instance;
}
// ...... 其它代碼
}
上面的代碼適用于單線程的程序,在多線程的程序下,以下的寫法在多個(gè)線程的競爭資源下,將仍有可能產(chǎn)生兩個(gè)以上的對象,存在線程安全性問題,例如下面的情況:
Thread1: if(instance == null) // true
Thread2: if(instance == null) // true
Thread1: instance = new Singleton(); // 產(chǎn)生一個(gè)實(shí)例
Thread2: instance = new Singleton(); // 又產(chǎn)生一個(gè)實(shí)例
Thread1: return instance; // 返回一個(gè)實(shí)例
Thread2: return instance; // 又返回一個(gè)實(shí)例
在多線程環(huán)境下,為了了避免資源同時(shí)競爭而導(dǎo)致如上產(chǎn)生多個(gè)實(shí)例的情況,我們加上同步機(jī)制:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
synchronized static public Singleton getInstance() {//為獲得系統(tǒng)唯一實(shí)例的靜態(tài)方法加上線程同步機(jī)制,保證此方法同時(shí)只能被一線程調(diào)用
if (instance == null) instance = new Singleton();
return instance;
} }
第三節(jié) 使用工廠模式實(shí)現(xiàn)DAO
由于J2EE模式眾多,篇幅有限,這里只概要介紹其中的一種使用工廠模式實(shí)現(xiàn)數(shù)據(jù)訪問對象。
使用數(shù)據(jù)訪問對象(DAO)來抽象和封裝所有對數(shù)據(jù)源的訪問。 DAO管理著與數(shù)據(jù)源的連接以便于檢索和存儲數(shù)據(jù),DAO實(shí)現(xiàn)了用來操作數(shù)據(jù)源的訪問機(jī)制。依賴于DAO的業(yè)務(wù)組件為其客戶端使用DAO提供了更簡單的接口,DAO完全向客戶端隱藏了數(shù)據(jù)源實(shí)現(xiàn)細(xì)節(jié)。由于當(dāng)?shù)蛯訑?shù)據(jù)源實(shí)現(xiàn)變化時(shí),DAO向客戶端提供的接口不會變化,所以該模式允許DAO調(diào)整到不同的存儲模式,而不會影響其客戶端或業(yè)務(wù)組件。重要的是,DAO充當(dāng)組件和數(shù)據(jù)源之間的適配器。
當(dāng)?shù)蛯哟鎯﹄S著實(shí)現(xiàn)的變化而變化時(shí),策略可以通過使用抽象工廠模式而實(shí)現(xiàn)。抽象工廠可以基于工廠方法實(shí)現(xiàn)而創(chuàng)建,并可使用工廠方法實(shí)現(xiàn),該策略提供一個(gè) DAO的抽象工廠對象,其中該對象可以構(gòu)造多種類型的具體的DAO工廠,每個(gè)工廠支持一種不同類型的持久性存儲實(shí)現(xiàn)。一旦你獲取某特定實(shí)現(xiàn)的具體DAO工廠,你可以使用它來生成該實(shí)現(xiàn)中所支持和實(shí)現(xiàn)的DAO,如下面類圖所示。
例如在一個(gè)項(xiàng)目中有四張表,分別是用戶信息表(user Info),論壇表(Forum),主題表(Topic),回復(fù)表(Reply)。先創(chuàng)建與數(shù)據(jù)庫表相對應(yīng)組件Java Bean,然后為每一個(gè)數(shù)據(jù)庫表配置一個(gè)數(shù)據(jù)庫操作接口。如下所示:
UserInfo訪問對象接口
package com.webclass.dao;
public interface UserInfoDao {
//設(shè)置帖子屬性時(shí),用戶的屬性
public void upUserState(String state,String usrName);
// 查詢用戶是否存在判斷(返回整個(gè)用戶)
public UserInfo selectUser(String usrName);
//設(shè)置用戶權(quán)限
public void upUserRole(int i ,String usrRole,int usrId);
//修改用戶信息
public boolean editUser(UserInfo user);
……………………………等操作邏輯方法
}
Forum訪問對象接口
package com.webclass.dao;
public interface ForumDao {
…………………操作邏輯方法
}
Reply訪問對象接口
package com.webclass.dao;
public interface ReplyDao {
………………操作邏輯方法
}
Topic訪問對象接口
package com.webclass.dao;
public interface TopicDao {
………………操作邏輯方法
}
接下來就是寫具體類實(shí)現(xiàn)以上其接口,而接口方法不依賴于類的具體實(shí)現(xiàn)。數(shù)據(jù)庫的訪問對象已經(jīng)創(chuàng)建好之后,應(yīng)用程序?qū)⒄{(diào)用這些對象進(jìn)行數(shù)據(jù)庫操作,如何調(diào)用這些對象呢?我們使用一個(gè)工廠類來專門負(fù)責(zé)這些對象的創(chuàng)建工作:
public class DBFactory {
public static ForumDao dBForum=null;
public static ReplyDao dBReply=null;
public static TopicDao dBTopic=null;
public static UserInfoDao dBUserInfo=null;
synchronized public static ForumDao getDBForum(){
if(dBForum==null) dBForum=new DBForum();
return dBForum;
}
synchronized public static ReplyDao getDBReply(){
if(dBReply==null) dBReply=new DBReply();
return dBReply;
}
synchronized public static TopicDao getDBTopic(){
if(dBTopic==null) dBTopic=new DBTopic();
return dBTopic;
}
synchronized public static UserInfoDao getDBUserInfo(){
if(dBUserInfo==null) dBUserInfo=new DBUserInfo();
return dBUserInfo;
}
}
這樣應(yīng)用程序的上層組件就可以直接調(diào)用DBFactory工廠對象所持有的靜態(tài)對象來進(jìn)來數(shù)據(jù)庫操作了,更重要的是當(dāng)?shù)讓訑?shù)據(jù)庫訪問操作發(fā)生改變時(shí)只需修改訪問對象,而不會波及到上層組件,使用軟件健壯性和靈活性大大提高。
總結(jié)
即使是利用當(dāng)今最先進(jìn)的軟件平臺J2EE,開發(fā)企業(yè)應(yīng)用程序仍然是一個(gè)難題,因?yàn)檐浖膹?fù)雜性和脆弱性無法回避。J2EE通過J2EE API提供了技術(shù)與服務(wù)的高層抽象,使企業(yè)開發(fā)得到了一定的簡化。但是,僅僅知道J2EE API是不夠的。要設(shè)計(jì)良好的體系結(jié)構(gòu),得到高質(zhì)量的應(yīng)用程序,要知道何時(shí)如何正確的使用J2EE API,就要使用更為實(shí)用的方式。
計(jì)算機(jī)技術(shù)更新發(fā)展很快,新技術(shù)方面由于經(jīng)驗(yàn)缺乏,通常我們要自己猜測如何正確使用這些技術(shù),要通過不斷的試驗(yàn),直到找出最佳的方法,最佳的方法顯然是從實(shí)踐中得到的,不是發(fā)明出來的,而是發(fā)現(xiàn)和不斷完善的。
工程學(xué)產(chǎn)中的一大原則就是總結(jié)經(jīng)驗(yàn)和利用實(shí)踐證明行之有效的方案,軟件開發(fā)也是這樣。經(jīng)驗(yàn)有助于更快更順利的建立良好的解決方案,從而節(jié)省成本,提高質(zhì)量。唯一的問題就是要需要獲得經(jīng)驗(yàn),但這個(gè)經(jīng)驗(yàn)可以從別人那里間接獲得,而不一定需要自己的直接經(jīng)驗(yàn)。別人的經(jīng)驗(yàn)如何描述?多年來,模式已經(jīng)成為收集,規(guī)范和分析某些情境中常見問題的有效方法,學(xué)習(xí)模式可以節(jié)省自己的時(shí)間,知道如何根據(jù)許多開發(fā)人員的間接經(jīng)驗(yàn)進(jìn)行合理的設(shè)計(jì)。
結(jié)束語
在撰寫這篇論文期間,我查閱了大量的技術(shù)書籍和文章,學(xué)到了很多的知識,使自己的技術(shù)水平得到了提高。同時(shí)得到了袁健美老師的悉心指導(dǎo)和幫助,在此,我向袁健美老師表達(dá)誠摯的謝意。
參考文獻(xiàn):
1. Bruce Eckel. Think in Java第三版 .電子工業(yè)出版社. 北京. 2002
2. Khawar Zaman Ahmed. J2EE和UML開發(fā)Java企業(yè)級應(yīng)用程序. 清華大學(xué)出版社. 2003
3. Crig Bery. 實(shí)用J2EE設(shè)計(jì)模式編程指南. www.china-pub.com. 2003
4. 孫衛(wèi)琴. Tomcat與Java Web開發(fā)技術(shù). 電子工業(yè)出版社. 2004
5. 孫衛(wèi)琴. 精通Struts. 電子工業(yè)出版社. 2004
6. Christopher Alexande. The Timeless Way of Building. Arrangement with Oxford University Press, Inc. 2003
7. 夏昕. Hibernate開發(fā)指南. www.china-pub.com. 2004