版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、<p> 2 Jive與設(shè)計(jì)模式</p><p> Jive論壇系統(tǒng)使用大量設(shè)計(jì)模式巧妙地實(shí)現(xiàn)了一系列功能。因?yàn)樵O(shè)計(jì)模式的通用性和可理解性,將幫助更多人很快地理解 Jive論壇源碼,從而可以依據(jù)一種“協(xié)定”來(lái)動(dòng)態(tài)地?cái)U(kuò)展它。那么使用設(shè)計(jì)模式還有哪些好處?</p><p><b> 2.1 設(shè)計(jì)模式</b></p>&
2、lt;p> 設(shè)計(jì)模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。毫無(wú)疑問(wèn),設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的。設(shè)計(jì)模式使代碼編制真正工程化,設(shè)計(jì)模式是軟件工程的基石。</p><p> GOF(設(shè)計(jì)模式作者簡(jiǎn)稱)《設(shè)計(jì)模式》這本書第一次將設(shè)計(jì)模式提升到理論高度,并將之規(guī)范化,該書提出了23種基本設(shè)計(jì)模式。自此,
3、在可復(fù)用面向?qū)ο筌浖陌l(fā)展過(guò)程中,新的大量的設(shè)計(jì)模式不斷出現(xiàn)。</p><p> 很多人都知道Java是完全面向?qū)ο蟮脑O(shè)計(jì)和編程語(yǔ)言,但是由于接受教育以及經(jīng)驗(yàn)的原因,大多數(shù)程序員或設(shè)計(jì)人員都是從傳統(tǒng)的過(guò)程語(yǔ)言轉(zhuǎn)變而來(lái),因此在思維習(xí)慣上要完全轉(zhuǎn)變?yōu)槊嫦驅(qū)ο蟮脑O(shè)計(jì)和開發(fā)方式是困難的,而學(xué)習(xí)設(shè)計(jì)模式可以更好地幫助和堅(jiān)固這種轉(zhuǎn)變。</p><p> 凡是學(xué)習(xí)完成設(shè)計(jì)模式的人都有一種類似重生的感
4、覺,這種重生可以從很多方面去解釋。換一種新的角度來(lái)看待和解決問(wèn)題應(yīng)該是一種比較貼切的解釋,而這種新的思維角度培養(yǎng)屬于基礎(chǔ)培訓(xùn),因此,設(shè)計(jì)模式是學(xué)習(xí)Java的必讀基礎(chǔ)課程之一。</p><p> 由于設(shè)計(jì)模式概念比較抽象,對(duì)于初學(xué)者學(xué)習(xí)有一定的難度,因此結(jié)合Jive論壇系統(tǒng)學(xué)習(xí)設(shè)計(jì)模式將是一種很好的選擇。</p><p> 掌握了設(shè)計(jì)模式,將會(huì)幫助程序員或設(shè)計(jì)人員以更加可重用性、可伸縮性
5、的眼光來(lái)開發(fā)應(yīng)用系統(tǒng),甚至開發(fā)通用的框架系統(tǒng)。框架系統(tǒng)是構(gòu)成一類特定軟件可復(fù)用設(shè)計(jì)的一組相互協(xié)作的類,主要是對(duì)應(yīng)用系統(tǒng)中反復(fù)重用部分的提煉,類似一種模板,這是一種結(jié)構(gòu)性的模板。</p><p> 框架通常定義了應(yīng)用體系的整體結(jié)構(gòu)、類和對(duì)象的關(guān)系等設(shè)計(jì)參數(shù),以便于具體應(yīng)用實(shí)現(xiàn)者能集中精力于應(yīng)用本身的特定細(xì)節(jié)??蚣軓?qiáng)調(diào)設(shè)計(jì)復(fù)用,而設(shè)計(jì)模式最小的可重用單位,因此框架不可避免地會(huì)反復(fù)使用到設(shè)計(jì)模式。關(guān)于通用框架系統(tǒng)的設(shè)
6、計(jì)開發(fā)將在以后章節(jié)中討論。</p><p> 其實(shí)Jive論壇本身也形成了一個(gè)基于Web結(jié)構(gòu)的通用框架系統(tǒng),因?yàn)樗芏嘣O(shè)計(jì)思想是可以重用的,例如設(shè)定一個(gè)總體入口,通過(guò)入口檢查用戶的訪問(wèn)控制權(quán)限,當(dāng)然還有其他各方面的功能實(shí)現(xiàn)方式都是值得在其他系統(tǒng)中借鑒的,也正因?yàn)樗阅J降男问奖憩F(xiàn)出來(lái),這種可重用性和可借鑒性就更強(qiáng)。</p><p> 2.2 ForumFactory與工廠模
7、式</p><p> 工廠模式是GOF設(shè)計(jì)模式的主要常用模式,它主要是為創(chuàng)建對(duì)象提供了一種接口,工廠模式主要是封裝了創(chuàng)建對(duì)象的細(xì)節(jié)過(guò)程,從而使得外界調(diào)用一個(gè)對(duì)象時(shí),根本無(wú)需關(guān)心這個(gè)對(duì)象是如何產(chǎn)生的。</p><p> 在GOF設(shè)計(jì)模式中,工廠模式分為工廠方法模式和抽象工廠模式。兩者主要區(qū)別是,工廠方法是創(chuàng)建一種產(chǎn)品接口下的產(chǎn)品對(duì)象,而抽象工廠模式是創(chuàng)建多種產(chǎn)品接口下的產(chǎn)品對(duì)象,非常類似
8、Builder生成器模式。在平時(shí)實(shí)踐中,使用較多的基本是工廠方法模式。</p><p> 以類SampleOne為例,要?jiǎng)?chuàng)建SampleOne的對(duì)象實(shí)例:</p><p> SampleOne sampleOne = new SampleOne();</p><p> 如果Sample類有幾個(gè)相近的類:SampleTwo或SampleThree,那么創(chuàng)建它們的
9、實(shí)例分別是:</p><p> SampleTwo sampleTwo = new SampleTwo();</p><p> SampleThree sampleThree = new SampleThree();</p><p> 其實(shí)這3個(gè)類都有一些共同的特征,如網(wǎng)上商店中銷售書籍、玩具或者化妝品。雖然它們是不同的具體產(chǎn)品,但是它們有一個(gè)共同特征,可以抽
10、象為“商品”。日常生活中很多東西都可以這樣高度抽象成一種接口形式。上面這3個(gè)類如果可以抽象為一個(gè)統(tǒng)一接口SampleIF,那么上面語(yǔ)句就可以成為:</p><p> SampleIF sampleOne = new SampleOne();</p><p> SampleIF sampleTwo = new SampleTwo();</p><p> Samp
11、leIF sampleThree = new SampleThree();</p><p> 在實(shí)際情況中,有時(shí)并不需要同時(shí)生成3種對(duì)象,而是根據(jù)情況在3者之中選一個(gè)。在這種情況下,需要使用工廠方法來(lái)完成了,創(chuàng)建一個(gè)叫SampleFactory的抽象類:</p><p> public class SampleFactory{</p><p> public
12、abstract SampleIF creator();</p><p><b> }</b></p><p> 在這個(gè)抽象工廠類中有一個(gè)抽象方法creator,但是沒有具體實(shí)現(xiàn),而是延遲到它的子類中實(shí)現(xiàn),創(chuàng)建子類SampleFactoryImp:</p><p> public class SampleFactoryImp extend
13、s SampleFactory{</p><p> public SampleIF creator(){</p><p> //根據(jù)其他因素綜合判斷返回具體產(chǎn)品</p><p> //假設(shè)應(yīng)該返回SampleOne對(duì)象</p><p> return new SampleOne();</p><p><b
14、> }</b></p><p><b> }</b></p><p> 在SampleFactoryImp中根據(jù)具體情況來(lái)選擇返回SampleOne、SampleTwo或SampleThree。所謂具體情況有很多種:上下文其他過(guò)程計(jì)算結(jié)果;直接根據(jù)配置文件中配置。</p><p> 上述工廠方法模式中涉及到一個(gè)抽象產(chǎn)品
15、接口Sample,如果還有其他完全不同的產(chǎn)品接口,如Product等,一個(gè)子類SampleFactoryImp只能實(shí)現(xiàn)一套系列產(chǎn)品方案的生產(chǎn),如果還需要另外一套系統(tǒng)產(chǎn)品方案,就可能需要另外一個(gè)子類SampleFactoryImpTwo來(lái)實(shí)現(xiàn)。這樣,多個(gè)產(chǎn)品系列、多個(gè)工廠方法就形成了抽象工廠模式。</p><p> 前面已經(jīng)討論在Jive中設(shè)置了論壇統(tǒng)一入口,這個(gè)統(tǒng)一入口就是ForumFactory,以下是For
16、umFactory的主要代碼:</p><p> public abstract class ForumFactory {</p><p> private static Object initLock = new Object();</p><p> private static String className = " com.Yasna.for
17、um.database.DbForumFactory";</p><p> private static ForumFactory factory = null; </p><p> public static ForumFactory getInstance(Authorization authorization) {</p><p> if (a
18、uthorization == null) {</p><p> return null;</p><p><b> }</b></p><p> //以下使用了Singleton 單態(tài)模式,將在2.3節(jié)討論</p><p> if (factory == null) {</p><p>
19、 synchronized(initLock) {</p><p> if (factory == null) {</p><p> ... //從配置文件中獲得當(dāng)前className</p><p><b> try {</b></p><p><b> //動(dòng)態(tài)裝載類</b></
20、p><p> Class c = Class.forName(className);</p><p> factory = (ForumFactory)c.newInstance();</p><p><b> }</b></p><p> catch (Exception e) {</p><p
21、> return null;</p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p> //返回 pr
22、oxy.用來(lái)限制授權(quán)對(duì)forum的訪問(wèn)</p><p> return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));</p><p><b> }</b></p><p> //創(chuàng)鍵產(chǎn)品接口Forum的具體對(duì)象實(shí)例&
23、lt;/p><p> public abstract Forum createForum(String name, String description)</p><p> throws UnauthorizedException, ForumAlreadyExistsException;</p><p> //創(chuàng)鍵產(chǎn)品接口ForumThread的具體對(duì)象實(shí)例&l
24、t;/p><p> public abstract ForumThread createThread(ForumMessage rootMessage) </p><p> throws UnauthorizedException;</p><p> //創(chuàng)鍵產(chǎn)品接口ForumMessage的具體對(duì)象實(shí)例</p><p> public
25、abstract ForumMessage createMessage();</p><p><b> ....</b></p><p><b> }</b></p><p> ForumFactory中提供了很多抽象方法如createForum、createThread和createMessage()等,它們是創(chuàng)建
26、各自產(chǎn)品接口下的具體對(duì)象,這3個(gè)接口就是前面分析的基本業(yè)務(wù)對(duì)象Forum、ForumThread和ForumMessage,這些創(chuàng)建方法在ForumFactory中卻不立即執(zhí)行,而是推遲到ForumFactory子類中實(shí)現(xiàn)。</p><p> ForumFactory的子類實(shí)現(xiàn)是com.Yasna.forum.database.DbForumFactory,這是一種數(shù)據(jù)庫(kù)實(shí)現(xiàn)方式。即在DbForumFactor
27、y中分別實(shí)現(xiàn)了在數(shù)據(jù)庫(kù)中createForum、createThread和createMessage()等3種方法,當(dāng)然也提供了動(dòng)態(tài)擴(kuò)展到另外一套系列產(chǎn)品的生產(chǎn)方案的可能。如果使用XML來(lái)實(shí)現(xiàn),那么可以編制一個(gè)XmlForumFactory的具體工廠子類來(lái)分別實(shí)現(xiàn)3種創(chuàng)建方法。</p><p> 因此,Jive論壇在統(tǒng)一入口處使用了抽象工廠模式來(lái)動(dòng)態(tài)地創(chuàng)建論壇中所需要的各種產(chǎn)品,如圖3-4所示。</p&g
28、t;<p> 圖3-4 ForumFactory抽象工廠模式圖</p><p> 圖3-4中,XmlForumFactory和DbForumFactory作為抽象工廠ForumFactory的兩個(gè)具體實(shí)現(xiàn),而Forum、ForumThread和ForumMessage分別作為3個(gè)系列抽象產(chǎn)品接口,依靠不同的工廠實(shí)現(xiàn)方式,會(huì)產(chǎn)生不同的產(chǎn)品對(duì)象。</p><p>
29、 從抽象工廠模式去理解Jive論壇統(tǒng)一入口處,可以一步到位掌握了幾個(gè)類之間的大概關(guān)系。因?yàn)槭褂昧顺橄蠊S模式這種通用的設(shè)計(jì)模式,可以方便源碼閱讀者快速地掌握整個(gè)系統(tǒng)的結(jié)構(gòu)和來(lái)龍去脈,圖3-4這張圖已經(jīng)初步展示了Jive的主要框架結(jié)構(gòu)。</p><p> 細(xì)心的讀者也許會(huì)發(fā)現(xiàn),在上面ForumFactory有一個(gè)getInstance比較令人費(fèi)解,這將在2.3節(jié)進(jìn)行討論。</p><p>
30、; 2.3 統(tǒng)一入口與單態(tài)模式</p><p> 在上面ForumFactory的getInstance方法使用單態(tài)(SingleTon)模式。單態(tài)模式是保證一個(gè)類有且僅有一個(gè)對(duì)象實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。</p><p> 前面曾提到ForumFactory是Jive提供客戶端訪問(wèn)數(shù)據(jù)庫(kù)系統(tǒng)的統(tǒng)一入口。為了保證所有的客戶端請(qǐng)求都要經(jīng)過(guò)這個(gè)ForumFactor
31、y,如果不使用單態(tài)模式,客戶端下列調(diào)用語(yǔ)句表示生成了ForumFactory實(shí)例:</p><p> ForumFactory factory = new DbForumFactory();</p><p> 客戶端每發(fā)生一次請(qǐng)求都調(diào)用這條語(yǔ)句,這就會(huì)發(fā)生每次都生成不同factory對(duì)象實(shí)例,這顯然不符合設(shè)計(jì)要求,因此必須使用單態(tài)模式。</p><p> 一般
32、在Java實(shí)現(xiàn)單態(tài)模式有幾種選擇,最常用而且安全的用法如下:</p><p> public class Singleton { </p><p> private Singleton(){}</p><p> //在自己內(nèi)部定義自己一個(gè)實(shí)例,是不是很奇怪</p><p> //注意這是private,只供內(nèi)部調(diào)用</p>
33、<p> private static Singleton instance = new Singleton(); </p><p> //這里提供了一個(gè)供外部訪問(wèn)本class的靜態(tài)方法,可以直接訪問(wèn)</p><p> public static Singleton getInstance() { </p><p> return instan
34、ce; </p><p><b> } </b></p><p><b> }</b></p><p> 單態(tài)模式一共使用了兩條語(yǔ)句實(shí)現(xiàn):第一條直接生成自己的對(duì)象,第二條提供一個(gè)方法供外部調(diào)用這個(gè)對(duì)象,同時(shí)最好將構(gòu)造函數(shù)設(shè)置為private,以防止其他程序員直接使用new Singleton生成實(shí)例。</p&g
35、t;<p> 還有一種Java單態(tài)模式實(shí)現(xiàn):</p><p> public class Singleton { </p><p> private Singleton(){}</p><p> private static Singleton instance = null; </p><p> public sta
36、tic synchronized Singleton getInstance() { </p><p> if (instance==null)</p><p> instance=new Singleton() </p><p> return instance; </p><p><b> }</b><
37、/p><p><b> ?。?lt;/b></p><p> 在上面代碼中,使用了判斷語(yǔ)句。如果instance為空,再進(jìn)行實(shí)例化,這成為lazy initialization。注意getInstance()方法的synchronized,這個(gè)synchronized很重要。如果沒有synchronized,那么使用getInstance()在第一次被訪問(wèn)時(shí)有可能得到多個(gè)Si
38、ngleton實(shí)例。</p><p> 關(guān)于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的討論,有興趣者可以進(jìn)一步研究。一般認(rèn)為第一種形式要更加安全些;但是后者可以用在類初始化時(shí)需要參數(shù)輸入的情況下。</p><p> 在Jive的ForumFactory中采取了后者lazy initialization形
39、式,這是為了能夠動(dòng)態(tài)配置指定ForumFactory的具體子類。在getInstance中,從配置文件中獲得當(dāng)前工廠的具體實(shí)現(xiàn),如果需要啟動(dòng)XmlForumFactory,就不必修改ForumFactory代碼,直接在配置文件中指定className的名字為XmlForumFactory。這樣通過(guò)下列動(dòng)態(tài)裝載機(jī)制生成ForumFactory具體對(duì)象:</p><p> Class c = Class.forNa
40、me(className);</p><p> factory = (ForumFactory)c.newInstance();</p><p> 這是利用Java的反射機(jī)制,可以通過(guò)動(dòng)態(tài)指定className的數(shù)值而達(dá)到生成對(duì)象的方式。</p><p> 使用單態(tài)模式的目標(biāo)是為了控制對(duì)象的創(chuàng)建,單態(tài)模式經(jīng)常使用在控制資源的訪問(wèn)上。例如數(shù)據(jù)庫(kù)連接或Socket
41、連接等。單態(tài)模式可以控制在某個(gè)時(shí)刻只有一個(gè)線程訪問(wèn)資源。由于Java中沒有全局變量的概念,因此使用單態(tài)模式有時(shí)可以起到這種作用,當(dāng)然需要注意是在一個(gè)JVM中。</p><p> 2.4 訪問(wèn)控制與代理模式</p><p> 仔細(xì)研究會(huì)發(fā)現(xiàn),在ForumFactory的getInstance方法中最后的返回值有些奇怪。按照單態(tài)模式的概念應(yīng)該直接返回factory這個(gè)對(duì)象實(shí)例,
42、但是卻返回了ForumFactoryProxy的一個(gè)實(shí)例,這實(shí)際上改變了單態(tài)模式的初衷。這樣客戶端每次通過(guò)調(diào)用ForumFactory的getInstance返回的就不是ForumFactory的惟一實(shí)例,而是新的對(duì)象。之所以這樣做是為了訪問(wèn)權(quán)限的控制,姑且不論這樣做的優(yōu)劣,先看看什么是代理模式。</p><p> 代理模式是屬于設(shè)計(jì)模式結(jié)構(gòu)型模式中一種,它是實(shí)際訪問(wèn)對(duì)象的代理對(duì)象,或者影子對(duì)象,主要達(dá)到控制實(shí)
43、際對(duì)象的訪問(wèn)。這種控制的目的很多,例如提高性能等。即遠(yuǎn)程代理模式,這種模式將在以后章節(jié)討論。</p><p> 其中一個(gè)主要的控制目的是控制客戶端對(duì)實(shí)際對(duì)象的訪問(wèn)權(quán)限。在Jive系統(tǒng)中,因?yàn)橛薪巧珯?quán)限的分別,對(duì)于Forum、ForumThread和FroumMessage的訪問(wèn)操作必須經(jīng)過(guò)權(quán)限機(jī)制驗(yàn)證后才能進(jìn)行。</p><p> 以ForumFactoryProxy中的createF
44、orum方法為例,其實(shí)ForumFactoryProxy也是FroumFactory的一種工廠實(shí)現(xiàn),它的createForum具體實(shí)現(xiàn)如下:</p><p> public Forum createForum(String name, String description)</p><p> throws UnauthorizedException, ForumAlreadyExist
45、sException</p><p><b> {</b></p><p> if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {</p><p> Forum newForum = factory.createForum(name, description);</p>
46、<p> return new ForumProxy(newForum, authorization, permissions);</p><p><b> }</b></p><p><b> else {</b></p><p> throw new UnauthorizedException();&
47、lt;/p><p><b> }</b></p><p><b> }</b></p><p> 在這個(gè)方法中進(jìn)行了權(quán)限驗(yàn)證,判斷是否屬于系統(tǒng)管理員。如果是,將直接從DbForumFactory對(duì)象factory的方法createForum中獲得一個(gè)新的Forum對(duì)象,然后再返回Forum的子類代理對(duì)象ForumProx
48、y。因?yàn)樵贔orum中也還有很多屬性和操作方法,這些也需要進(jìn)行權(quán)限驗(yàn)證。ForumProxy和ForumFactoryProxy起到類似的作用。</p><p> Jive中有下列幾個(gè)代理類:</p><p> ? ForumFactoryProxy:客戶端和DbForumFac
49、tory之間的代理??蛻舳嗽L問(wèn)DbForumFactory的任何方法都要先經(jīng)過(guò)ForumFactoryProxy相應(yīng)方法代理一次。以下意思相同。</p><p> ? ForumProxy:客戶端和DbForum之間的代理,研究Forum對(duì)象的每個(gè)方法,必須先看ForumProxy對(duì)象的方法。</p
50、><p> ? ForumMessageProxy:客戶端和DbForumMessage之間的代理。</p><p> ? ForumThreadProxy:客戶端和DbFor
51、umThread之間的代理。</p><p> User和Group也有相應(yīng)的代理類。</p><p> 由以上分析看出,每個(gè)數(shù)據(jù)對(duì)象都有一個(gè)代理。如果系統(tǒng)中數(shù)據(jù)對(duì)象非常多,依據(jù)這種一對(duì)一的代理關(guān)系,會(huì)有很多代理類,將使系統(tǒng)變得不是非常干凈,因此可以使用動(dòng)態(tài)代理來(lái)代替這所有的代理類,具體實(shí)現(xiàn)將在以后章節(jié)討論。</p><p> 2.5 批量分頁(yè)查詢
52、與迭代模式</p><p> 迭代(Iterator)模式是提供一種順序訪問(wèn)某個(gè)集合各個(gè)元素的方法,確保不暴露該集合的內(nèi)部表現(xiàn)。迭代模式應(yīng)用于對(duì)大量數(shù)據(jù)的訪問(wèn),Java Collection API中Iterator就是迭代模式的一種實(shí)現(xiàn)。</p><p> 在前面章節(jié)已經(jīng)討論過(guò),用戶查詢大量數(shù)據(jù),從數(shù)據(jù)庫(kù)不應(yīng)該直接返回ResultSet,應(yīng)該是Collection。但是有一個(gè)問(wèn)題,如
53、果這個(gè)數(shù)據(jù)很大,需要分頁(yè)面顯示。如果一下子將所有頁(yè)面要顯示的數(shù)據(jù)都查詢出來(lái)放在Collection,會(huì)影響性能。而使用迭代模式則不必將全部集合都展現(xiàn)出來(lái),只有遍歷到某個(gè)元素時(shí)才會(huì)查詢數(shù)據(jù)庫(kù)獲得這個(gè)元素的數(shù)據(jù)。</p><p> 以論壇中顯示帖子主題為例,在一個(gè)頁(yè)面中不可能顯示所有主題,只有分頁(yè)面顯示,如圖3-5所示。</p><p> 圖3-5中一共分15頁(yè)來(lái)顯示所有論壇帖子,可以從顯
54、示Forum.jsp中發(fā)現(xiàn)下列語(yǔ)句可以完成上述結(jié)果:</p><p> ResultFilter filter = new ResultFilter(); //設(shè)置結(jié)果過(guò)濾器</p><p> filter.setStartIndex(start);
55、60; //設(shè)置開始點(diǎn)</p><p> filter.setNumResults(range); //設(shè)置范圍</p><p> ForumThreadIterator threads = forum.threads(filter); //獲得迭代器&l
56、t;/p><p> while(threads.hasNext){</p><p> //逐個(gè)顯示threads中帖子主題,輸出圖3-5中的每一行 </p><p><b> }</b></p><p> 圖3-5 分頁(yè)顯示所有帖子</p><p> 上述代碼中主要是從Forum
57、的threads方法獲得迭代器ForumThreadIterator的實(shí)例,依據(jù)前面代理模式中分析、研究Forum對(duì)象的方法,首先是看ForumProxy中對(duì)應(yīng)方法,然后再看DbForum中對(duì)應(yīng)方法的具體實(shí)現(xiàn)。在ForumProxy中,threads方法如下:</p><p> public ForumThreadIterator threads(ResultFilter resultFilter) {<
58、/p><p> ForumThreadIterator iterator = forum.threads(resultFilter);</p><p> return new ForumThreadIteratorProxy(iterator, authorization, permissions);</p><p><b> }</b>&l
59、t;/p><p> 首先是調(diào)用了DbForum中具體的threads方法,再追蹤到DbForum中看看,它的threads方法代碼如下:</p><p> public ForumThreadIterator threads(ResultFilter resultFilter) {</p><p> //按resultFilter設(shè)置范圍要求獲得SQL查詢語(yǔ)句&l
60、t;/p><p> String query = getThreadListSQL(resultFilter, false); </p><p> //獲得resultFilter設(shè)置范圍內(nèi)的所有ThreadID集合</p><p> long [] threadBlock = getThreadBlock(query.toString(), resu
61、ltFilter.getStartIndex());</p><p> //以下是計(jì)算查詢區(qū)域的開始點(diǎn)和終點(diǎn)</p><p> int startIndex = resultFilter.getStartIndex();</p><p> int endIndex;</p><p> // If number of results i
62、s set to inifinite, set endIndex to the total</p><p> // number of threads in the forum.</p><p> if (resultFilter.getNumResults() == ResultFilter.NULL_INT) {</p><p> endIndex = (
63、int)getThreadCount(resultFilter);</p><p><b> }else {</b></p><p> endIndex = resultFilter.getNumResults() + startIndex;</p><p><b> }</b></p><p&g
64、t; return new ForumThreadBlockIterator(threadBlock, query.toString(),</p><p> startIndex, endIndex, this.id, factory);</p><p><b> }</b></p><p> ResultFilter是一個(gè)查詢結(jié)果類,
65、可以對(duì)論壇主題Thread和帖子內(nèi)容Message進(jìn)行過(guò)濾或排序,這樣就可以根據(jù)用戶要求定制特殊的查詢范圍。如查詢某個(gè)用戶去年在這個(gè)論壇發(fā)表的所有帖子,那只要?jiǎng)?chuàng)建一個(gè)ResultFilter對(duì)象就可以代表這個(gè)查詢要求。</p><p> 在上面threads方法代碼中,第一步是先定制出相應(yīng)的動(dòng)態(tài)SQL查詢語(yǔ)句,然后使用這個(gè)查詢語(yǔ)句查詢數(shù)據(jù)庫(kù),獲得查詢范圍內(nèi)所有的ForumThread的ID集合,然后在這個(gè)ID集
66、合中獲得當(dāng)前頁(yè)面的ID子集合,這是非常關(guān)鍵的一步。</p><p> 在這關(guān)鍵的一步中,有兩個(gè)重要的方法getThreadListSQL和getThreadBlock:</p><p> ? GetThreadListSQL:獲得SQL查詢語(yǔ)句query的值,這個(gè)方法Jive實(shí)現(xiàn)
67、起來(lái)顯得非常地瑣碎。</p><p> ? GetThreadBlock:獲得當(dāng)前頁(yè)面的ID子集合,那么如何確定ID子集合的開始位置呢?查看getThreadBlock方法代碼,可以發(fā)現(xiàn),它是使用最普遍的ResultSet next()方法來(lái)逐個(gè)跳躍到開始位置。</p><p>
68、 上面代碼的Threads方法中最后返回的是ForumThreadBlockIterator,它是抽象類ForumThreadIterator的子類,而ForumThreadIterator繼承了Collection的Iterator,以此聲明自己是一個(gè)迭代器,F(xiàn)orumMessageBlockIterator實(shí)現(xiàn)的具體方法如下:</p><p> public boolean hasNext();
69、 //判斷是否有下一個(gè)元素</p><p> public boolean hasPrevious() //判斷是否有前一個(gè)元素</p><p> public Object next() throws java.util.NoSuchElementException //獲得下一個(gè)元素實(shí)例</p><p&
70、gt; ForumThreadBlockIterator中的Block是“頁(yè)”的意思,它的一個(gè)主要類變量threadBlock包含的是一個(gè)頁(yè)面中所有ForumThread的ID,next()方法實(shí)際是對(duì)threadBlock中ForumThread進(jìn)行遍歷,如果這個(gè)頁(yè)面全部遍歷完成,將再獲取下一頁(yè)(Block)數(shù)據(jù)。</p><p> 在ForumThreadBlockIterator重要方法getEleme
71、nt中實(shí)現(xiàn)了兩個(gè)功能:</p><p> ? 如果當(dāng)前遍歷指針超過(guò)當(dāng)前頁(yè)面,將使用getThreadBlock獲得下一個(gè)頁(yè)面的ID子集合;</p><p> ? 如果當(dāng)前遍
72、歷指針在當(dāng)前頁(yè)面之內(nèi),根據(jù)ID獲得完整的數(shù)據(jù)對(duì)象,實(shí)現(xiàn)輸出;</p><p> ForumThreadBlockIterator的getElement方法代碼如下:</p><p> private Object getElement(int index) {</p><p> if (index < 0) {
73、0; return null; }</p><p> // 檢查所要獲得的 element 是否在本查詢范圍內(nèi)(當(dāng)前頁(yè)面內(nèi))</p><p> if (index < blockStart || </p><p> index >
74、= blockStart + DbForum.THREAD_BLOCK_SIZE) { </p><p><b> try {</b></p><p> //從緩沖中獲得Forum實(shí)例</p><p> DbForum forum = factory.cacheManager.forumCache.get(foru
75、mID);</p><p> //獲得下一頁(yè)的內(nèi)容</p><p> this.threadBlock = forum.getThreadBlock(query, index);</p><p> this.blockID = index / DbForum.THREAD_BLOCK_SIZE;</p><p> this.block
76、Start = blockID * DbForum.THREAD_BLOCK_SIZE;</p><p> } catch (ForumNotFoundException fnfe) {</p><p> return null;</p><p><b> }</b></p><p><b> }<
77、;/b></p><p> Object element = null;</p><p> // 計(jì)算這個(gè)元素在當(dāng)前查詢范圍內(nèi)的相對(duì)位置</p><p> int relativeIndex = index % DbForum.THREAD_BLOCK_SIZE;</p><p> // Make sure index isn&
78、#39;t too large</p><p> if (relativeIndex < threadBlock.length) {</p><p><b> try {</b></p><p> // 從緩沖中獲得實(shí)際thread 對(duì)象</p><p> element = factory.cacheMa
79、nager.threadCache.get(</p><p> threadBlock[relativeIndex]);</p><p> } catch (ForumThreadNotFoundException tnfe) { }</p><p><b> }</b></p><p> return elem
80、ent;</p><p><b> }</b></p><p> ForumThreadBlockIterator是真正實(shí)現(xiàn)分頁(yè)查詢的核心功能,F(xiàn)orumThreadBlockIterator對(duì)象返回到客戶端的過(guò)程中,遭遇ForumThreadIteratorProxy的截獲,可以回頭看看ForumProxy中的threads方法,它最終返回給調(diào)用客戶端Forum
81、.jsp的是ForumThreadIteratorProxy實(shí)例。</p><p> ForumThreadIteratorProxy也是迭代器ForumThreadIterator的一個(gè)子類,它的一個(gè)具體方法中:</p><p> public Object next() {</p><p> return new ForumThreadProxy((For
82、umThread)iterator.next(), authorization,</p><p> permissions);</p><p><b> }</b></p><p> 這一句是返回一個(gè)ForumThreadProxy實(shí)例,返回就是一個(gè)ForumThread實(shí)例的代理。這里,Jive使用代理模式實(shí)現(xiàn)訪問(wèn)控制實(shí)現(xiàn)得不是很巧妙,
83、似乎有代理到處“飛”的感覺,這是可以對(duì)之進(jìn)行改造的。</p><p> 從以上可以看出,Jive在輸出如圖3-5所示的多頁(yè)查詢結(jié)果時(shí),采取了下列步驟:</p><p> ?。?)先查詢出符合查詢條件的所有對(duì)象元素的ID集合,注意不是所有對(duì)象元素,只是其ID的集合,這樣節(jié)約了大量?jī)?nèi)存。</p><p> ?。?)每個(gè)頁(yè)面視為一個(gè)Block,每當(dāng)進(jìn)入下一頁(yè)時(shí),獲得下一
84、個(gè)頁(yè)面的所有對(duì)象的ID集合。</p><p> ?。?)輸出當(dāng)前頁(yè)面的所有對(duì)象時(shí),首先從緩沖中獲取,如果緩沖中沒有,再根據(jù)ID從數(shù)據(jù)庫(kù)中獲取完整的對(duì)象數(shù)據(jù)。</p><p> 上述實(shí)現(xiàn)方法完全基于即查即顯,相比于一般批量查詢做法:一次性獲得所有數(shù)據(jù),然后遍歷數(shù)據(jù)結(jié)果集ResultSet,Jive這種批量查詢方式是一種比較理想的選擇。</p><p> 以上是Fo
85、rumThread的批量顯示,有關(guān)帖子內(nèi)容ForumMessage也是采取類似做法。在每個(gè)ForumThread中可能有很多帖子內(nèi)容(ForumMessage對(duì)象集合),也不能在一個(gè)頁(yè)面中全部顯示,所以也是使用迭代模式來(lái)實(shí)現(xiàn)的。顯示一個(gè)Forum主題下所有帖子內(nèi)容的功能由ForumThread的messages()方法完成,檢查它的代理類FroumThreadProxy如何具體完成:</p><p> publ
86、ic Iterator messages(ResultFilter resultFilter) {</p><p> Iterator iterator = thread.messages(resultFilter);</p><p> return new IteratorProxy(JiveGlobals.MESSAGE, iterator, authorization, perm
87、issions);</p><p><b> }</b></p><p> 實(shí)現(xiàn)的原理基本相同,返回的都是一個(gè)Iterator代理類,在這些代理類中都是進(jìn)行用戶權(quán)限檢驗(yàn)的。</p><p> Jive中也有關(guān)于一次性獲得所有數(shù)據(jù),然后遍歷ResultSet的做法。這種做法主要適合一次性查詢數(shù)據(jù)庫(kù)的所有數(shù)據(jù),例如查詢當(dāng)前所有論壇Forum,
88、首先實(shí)現(xiàn)SQL語(yǔ)句:</p><p> SELECT forumID FROM jiveForum</p><p> 獲得所有Forum的forumID,這段代碼位于DbForumFactory.java的forums方法中,如下:</p><p> public Iterator forums() {</p><p> if (fo
89、rums == null) {</p><p> LongList forumList = new LongList();</p><p> Connection con = null;</p><p> PreparedStatement pstmt = null;</p><p><b> try {</b>
90、</p><p> con = ConnectionManager.getConnection();</p><p> // GET_FORUMS值是SELECT forumID FROM jiveForum</p><p> pstmt = con.prepareStatement(GET_FORUMS);</p><p> Res
91、ultSet rs = pstmt.executeQuery();</p><p> while (rs.next()) {</p><p> forumList.add(rs.getLong(1));
92、60; //將所有查詢ID結(jié)果放入forumList中</p><p><b> }</b></p><p> }catch (SQLException sqle) {</p><p> sqle.printStackTrace();</p><p> } finally {</p><p&g
93、t;<b> …</b></p><p><b> }</b></p><p> return new DatabaseObjectIterator(JiveGlobals.FORUM, forums, this);</p><p><b> }</b></p><p>
94、; forums方法是返回一個(gè)DatabaseObjectIterator,這個(gè)DatabaseObjectIterator也是一個(gè)迭代器,但是實(shí)現(xiàn)原理要比ForumThreadBlockIterator簡(jiǎn)單。它只提供了一個(gè)遍歷指針,在所有ID結(jié)果集中遍歷,然后也是通過(guò)ID獲得完整的數(shù)據(jù)對(duì)象。</p><p> 總之,Jive中關(guān)于批量查詢有兩種實(shí)現(xiàn)方式:以ForumThreadBlockIterator為代
95、表的實(shí)現(xiàn)方式適合在數(shù)據(jù)量巨大、需要多頁(yè)查詢時(shí)使用;而DatabaseObjectIterator則是推薦在一個(gè)頁(yè)面中顯示少量數(shù)據(jù)時(shí)使用。</p><p> 2.6 過(guò)濾器與裝飾模式</p><p> 裝飾(Decorator)模式是動(dòng)態(tài)給一個(gè)對(duì)象添加一些額外的職責(zé),或者說(shuō)改變這個(gè)對(duì)象的一些行為。這就類似于使用油漆為某個(gè)東西刷上油漆,在原來(lái)的對(duì)象表面增加了一層外衣。</
96、p><p> 在裝飾模式中,有兩個(gè)主要角色:一個(gè)是被刷油漆的對(duì)象(decoratee);另外一個(gè)是給decoratee刷油漆的對(duì)象(decorator)。這兩個(gè)對(duì)象都繼承同一個(gè)接口。</p><p> 首先舉一個(gè)簡(jiǎn)單例子來(lái)說(shuō)明什么是裝飾模式。</p><p><b> 先創(chuàng)建一個(gè)接口:</b></p><p> pu
97、blic interface Work</p><p><b> { </b></p><p> public void insert();</p><p><b> }</b></p><p> 這是一種打樁工作的抽象接口,動(dòng)作insert表示插入,那么插入什么?下面這個(gè)實(shí)現(xiàn)表示方形木樁的
98、插入:</p><p> public class SquarePeg implements Work{</p><p> public void insert(){</p><p> System.out.println("方形樁插入");</p><p><b> } </b></p
99、><p><b> }</b></p><p> 本來(lái)這樣也許就可以滿足打樁的工作需要,但是有可能土質(zhì)很硬,在插入方形樁之前先要打一個(gè)洞,那么又將如何實(shí)現(xiàn)?可以編制一個(gè)Decorator類,同樣繼承Work接口,但是在實(shí)現(xiàn)insert方法時(shí)有些特別:</p><p> public class Decorator implements Wor
100、k{</p><p> private Work work;</p><p> //額外增加的功能被打包在這個(gè)List中</p><p> private ArrayList others = new ArrayList(); </p><p> public Decorator(Work work)</p><p
101、><b> {</b></p><p> this.work=work;</p><p> others.add("打洞"); //準(zhǔn)備好額外的功能</p><p><b> }</b></p><p> public void inser
102、t(){</p><p> otherMethod();</p><p> work.insert();</p><p><b> } </b></p><p> public void otherMethod()</p><p><b> {</b></p&
103、gt;<p> ListIterator listIterator = others.listIterator();</p><p> while (listIterator.hasNext())</p><p><b> {</b></p><p> System.out.println(((String)(listIt
104、erator.next())) + " 正在進(jìn)行");</p><p><b> }</b></p><p><b> } </b></p><p><b> }</b></p><p> 在Decorator的方法insert中先執(zhí)行otherMe
105、thod()方法,然后才實(shí)現(xiàn)SquarePeg的insert方法。油漆工Decorator給被油漆者SquarePeg添加了新的行為——打洞。具體客戶端調(diào)用如下:</p><p> Work squarePeg = new SquarePeg();</p><p> Work decorator = new Decorator(squarePeg);</p><p&
106、gt; decorator.insert();</p><p> 本例中只添加了一個(gè)新的行為(打洞),如果還有很多類似的行為,那么使用裝飾模式的優(yōu)點(diǎn)就體現(xiàn)出來(lái)了。因?yàn)榭梢酝ㄟ^(guò)另外一個(gè)角度(如組織新的油漆工實(shí)現(xiàn)子類)來(lái)對(duì)這些行為進(jìn)行混合和匹配,這樣就不必為每個(gè)行為創(chuàng)建一個(gè)類,從而減少了系統(tǒng)的復(fù)雜性。</p><p> 使用裝飾模式可以避免在被油漆對(duì)象decoratee中包裝很多動(dòng)態(tài)的,
107、可能需要也可能不需要的功能,只要在系統(tǒng)真正運(yùn)行時(shí),通過(guò)油漆工decorator來(lái)檢查那些需要加載的功能,實(shí)行動(dòng)態(tài)加載。</p><p> Jive論壇實(shí)現(xiàn)了信息過(guò)濾功能。例如可以將帖子內(nèi)容中的HTML語(yǔ)句過(guò)濾掉;可以將帖子內(nèi)容中Java代碼以特別格式顯示等。這些過(guò)濾功能有很多,在實(shí)際使用時(shí)不一定都需要,是由實(shí)際情況選擇的。例如有的論壇就不需要將帖子內(nèi)容的HTML語(yǔ)句過(guò)濾掉,選擇哪些過(guò)濾功能是由論壇管理者具體動(dòng)態(tài)
108、決定的。而且新的過(guò)濾功能可能隨時(shí)可以定制開發(fā)出來(lái),如果試圖強(qiáng)行建立一種接口包含所有過(guò)濾行為,那么到時(shí)有新過(guò)濾功能加入時(shí),還需要改變接口代碼,真是一種危險(xiǎn)的行為。</p><p> 裝飾模式可以解決這種運(yùn)行時(shí)需要?jiǎng)討B(tài)增加功能的問(wèn)題,且看看Jive是如何實(shí)現(xiàn)的。</p><p> 前面討論過(guò),在Jive中,有主要幾個(gè)對(duì)象ForumFactory、Forum以及ForumThread和For
109、umMessage,它們之間的關(guān)系如圖3-2所示。因此帖子內(nèi)容ForumMessage對(duì)象的獲得是從其上級(jí)FroumThread的方法getMessage中獲取,但是在實(shí)際代碼中,F(xiàn)orumThread的方法getMessage委托ForumFactory來(lái)獲取ForumMessage對(duì)象??纯碏orumThread的子類DbForumThread的getMessage代碼:</p><p> public F
110、orumMessage getMessage(long messageID)</p><p> throws ForumMessageNotFoundException</p><p><b> {</b></p><p> return factory.getMessage(messageID, this.id, forumID);&l
111、t;/p><p><b> }</b></p><p> 這是一種奇怪的委托,大概是因?yàn)樾枰紤]到過(guò)濾器功能有意為之吧。那就看看ForumFactory的具體實(shí)現(xiàn)子類DbForumFactory的getMessage功能,getMessage是將數(shù)據(jù)庫(kù)中的ForumMessage對(duì)象經(jīng)由過(guò)濾器過(guò)濾一遍后輸出(注:因?yàn)樵瓉?lái)的Jive的getMessage代碼考慮到可緩存
112、或不可緩存的過(guò)濾,比較復(fù)雜,實(shí)際過(guò)濾功能都是可以緩存的,因此精簡(jiǎn)如下)。</p><p> protected ForumMessage getMessage(long messageID, long threadID, long forumID)</p><p> throws ForumMessageNotFoundException</p><p><
113、;b> {</b></p><p> DbForumMessage message = cacheManager.messageCache.get(messageID);</p><p> // Do a security check to make sure the message comes from the thread.</p><p&g
114、t; if (message.threadID != threadID) {</p><p> throw new ForumMessageNotFoundException();</p><p><b> }</b></p><p> ForumMessage filterMessage = null;</p><
115、p><b> try {</b></p><p> // 應(yīng)用全局過(guò)濾器</p><p> filterMessage = filterManager.applyFilters(message);</p><p> Forum forum = getForum(forumID);
116、160; </p><p> //應(yīng)用本論壇過(guò)濾器</p><p> filterMessage = forum.getFilterManager().applyFilters(filterMessage);</p><p><b>
117、 }</b></p><p> catch (Exception e) { }</p><p> return filterMessage;</p><p><b> }</b></p><p> 上面代碼實(shí)際是裝飾模式的客戶端調(diào)用代碼,DbForumMessage 的實(shí)例message是被油漆者d
118、ecoratee。通過(guò)filterManager 或forum.getFilterManager()的applyFilter方法,將message實(shí)行了所有的過(guò)濾功能。這就類似前面示例的下列語(yǔ)句:</p><p> Work decorator = new Decorator(squarePeg);</p><p> forum.getFilterManager()是從數(shù)據(jù)庫(kù)中獲取當(dāng)前
119、配置的所有過(guò)濾器類。每個(gè)Forum都有一套自己的過(guò)濾器類,這是通過(guò)下列語(yǔ)句實(shí)現(xiàn)的:</p><p> FilterManager filterManager = new DbFilterManager();</p><p> 在DbFilterManager 的類變量ForumMessageFilter [] filters中保存著所有的過(guò)濾器,applyFilters方法
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫(kù)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- java種設(shè)計(jì)模式總結(jié)
- java23種設(shè)計(jì)模式(總結(jié))
- java設(shè)計(jì)模式瘋狂java聯(lián)盟版
- java設(shè)計(jì)模式之外觀
- java23種設(shè)計(jì)模式
- java幾種模式
- java課程設(shè)計(jì)---基于java的坦克大戰(zhàn)
- 基于JAVA語(yǔ)言的設(shè)計(jì)模式識(shí)別的研究與應(yīng)用.pdf
- java課程設(shè)計(jì)----基于java的坦克大戰(zhàn)
- java從菜鳥到高手之設(shè)計(jì)模式
- 基于IDE模式的Java實(shí)驗(yàn)平臺(tái)研究.pdf
- 基于java課設(shè)設(shè)計(jì)
- java從菜鳥到高手之設(shè)計(jì)模式
- 用Java泛型實(shí)現(xiàn)設(shè)計(jì)模式.pdf
- 畢業(yè)設(shè)計(jì)----基于bs模式的java在線考試系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
- 基于Java與Web模式的教務(wù)管理系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn).pdf
- java課程設(shè)計(jì)--基于java的24點(diǎn)游戲
- java課程設(shè)計(jì)--基于java的掃雷游戲軟件設(shè)計(jì)
- 基于java會(huì)議管理系統(tǒng)設(shè)計(jì)
- 基于java的cs模式網(wǎng)絡(luò)聊天室的
評(píng)論
0/150
提交評(píng)論