每日一得

          不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速開發(fā)
          最近關(guān)心的內(nèi)容:SSH,seam,flex,敏捷,TDD
          本站的官方站點是:顛覆軟件

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            220 隨筆 :: 9 文章 :: 421 評論 :: 0 Trackbacks

          2? Jive 與設(shè)計模式

          Jive 論壇系統(tǒng)使用大量設(shè)計模式巧妙地實現(xiàn)了一系列功能。因為設(shè)計模式的通用性和可理解性,將幫助更多人很快地理解 Jive 論壇源碼,從而可以依據(jù)一種“協(xié)定”來動態(tài)地擴展它。那么使用設(shè)計模式還有哪些好處?

          2.1? 設(shè)計模式

          設(shè)計模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié)。使用設(shè)計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。毫無疑問,設(shè)計模式于己于他人于系統(tǒng)都是多贏的。設(shè)計模式使代碼編制真正工程化,設(shè)計模式是軟件工程的基石。

          GOF (設(shè)計模式作者簡稱)《設(shè)計模式》這本書第一次將設(shè)計模式提升到理論高度,并將之規(guī)范化,該書提出了 23 種基本設(shè)計模式。自此,在可復(fù)用面向?qū)ο筌浖陌l(fā)展過程中,新的大量的設(shè)計模式不斷出現(xiàn)。

          很多人都知道 Java 是完全面向?qū)ο蟮脑O(shè)計和編程語言,但是由于接受教育以及經(jīng)驗的原因,大多數(shù)程序員或設(shè)計人員都是從傳統(tǒng)的過程語言轉(zhuǎn)變而來,因此在思維習(xí)慣上要完全轉(zhuǎn)變?yōu)槊嫦驅(qū)ο蟮脑O(shè)計和開發(fā)方式是困難的,而學(xué)習(xí)設(shè)計模式可以更好地幫助和堅固這種轉(zhuǎn)變。

          凡是學(xué)習(xí)完成設(shè)計模式的人都有一種類似重生的感覺,這種重生可以從很多方面去解釋。換一種新的角度來看待和解決問題應(yīng)該是一種比較貼切的解釋,而這種新的思維角度培養(yǎng)屬于基礎(chǔ)培訓(xùn),因此,設(shè)計模式是學(xué)習(xí) Java 的必讀基礎(chǔ)課程之一。

          由于設(shè)計模式概念比較抽象,對于初學(xué)者學(xué)習(xí)有一定的難度,因此結(jié)合 Jive 論壇系統(tǒng)學(xué)習(xí)設(shè)計模式將是一種很好的選擇。

          掌握了設(shè)計模式,將會幫助程序員或設(shè)計人員以更加可重用性、可伸縮性的眼光來開發(fā)應(yīng)用系統(tǒng),甚至開發(fā)通用的框架系統(tǒng)??蚣芟到y(tǒng)是構(gòu)成一類特定軟件可復(fù)用設(shè)計的一組相互協(xié)作的類,主要是對應(yīng)用系統(tǒng)中反復(fù)重用部分的提煉,類似一種模板,這是一種結(jié)構(gòu)性的模板。

          框架通常定義了應(yīng)用體系的整體結(jié)構(gòu)、類和對象的關(guān)系等設(shè)計參數(shù),以便于具體應(yīng)用實現(xiàn)者能集中精力于應(yīng)用本身的特定細(xì)節(jié)??蚣軓娬{(diào)設(shè)計復(fù)用,而設(shè)計模式最小的可重用單位,因此框架不可避免地會反復(fù)使用到設(shè)計模式。關(guān)于通用框架系統(tǒng)的設(shè)計開發(fā)將在以后章節(jié)中討論。

          其實 Jive 論壇本身也形成了一個基于 Web 結(jié)構(gòu)的通用框架系統(tǒng),因為它很多設(shè)計思想是可以重用的,例如設(shè)定一個總體入口,通過入口檢查用戶的訪問控制權(quán)限,當(dāng)然還有其他各方面的功能實現(xiàn)方式都是值得在其他系統(tǒng)中借鑒的,也正因為它以模式的形式表現(xiàn)出來,這種可重用性和可借鑒性就更強。

          2.2? ForumFactory 與工廠模式

          工廠模式是 GOF 設(shè)計模式的主要常用模式,它主要是為創(chuàng)建對象提供了一種接口,工廠模式主要是封裝了創(chuàng)建對象的細(xì)節(jié)過程,從而使得外界調(diào)用一個對象時,根本無需關(guān)心這個對象是如何產(chǎn)生的。

          GOF 設(shè)計模式中,工廠模式分為工廠方法模式和抽象工廠模式。兩者主要區(qū)別是,工廠方法是創(chuàng)建一種產(chǎn)品接口下的產(chǎn)品對象,而抽象工廠模式是創(chuàng)建多種產(chǎn)品接口下的產(chǎn)品對象,非常類似 Builder 生成器模式。在平時實踐中,使用較多的基本是工廠方法模式。

          以類 SampleOne 為例,要創(chuàng)建 SampleOne 的對象實例 :

          SampleOne sampleOne = new SampleOne();

          如果 Sample 類有幾個相近的類: SampleTwo SampleThree ,那么創(chuàng)建它們的實例分別是:

          SampleTwo sampleTwo = new SampleTwo();

          SampleThree sampleThree = new SampleThree();

          其實這 3 個類都有一些共同的特征,如網(wǎng)上商店中銷售書籍、玩具或者化妝品。雖然它們是不同的具體產(chǎn)品,但是它們有一個共同特征,可以抽象為“商品”。日常生活中很多東西都可以這樣高度抽象成一種接口形式。上面這 3 個類如果可以抽象為一個統(tǒng)一接口 SampleIF ,那么上面語句就可以成為:

          SampleIF sampleOne = new SampleOne();

          SampleIF sampleTwo = new SampleTwo();

          SampleIF sampleThree = new SampleThree();

          在實際情況中,有時并不需要同時生成 3 種對象,而是根據(jù)情況在 3 者之中選一個。在這種情況下,需要使用工廠方法來完成了,創(chuàng)建一個叫 SampleFactory 的抽象類:

          public class SampleFactory{

          ?? public abstract SampleIF creator();

          }

          在這個抽象工廠類中有一個抽象方法 creator ,但是沒有具體實現(xiàn),而是延遲到它的子類中實現(xiàn),創(chuàng)建子類 SampleFactoryImp

          public class SampleFactoryImp extends SampleFactory{

          ?? public SampleIF creator(){

          ??? // 根據(jù)其他因素綜合判斷返回具體產(chǎn)品

          ??? // 假設(shè)應(yīng)該返回 SampleOne 對象

          ?????? return new SampleOne();

          }

          }

          SampleFactoryImp 中根據(jù)具體情況來選擇返回 SampleOne 、 SampleTwo SampleThree 。所謂具體情況有很多種:上下文其他過程計算結(jié)果;直接根據(jù)配置文件中配置。

          上述工廠方法模式中涉及到一個抽象產(chǎn)品接口 Sample ,如果還有其他完全不同的產(chǎn)品接口,如 Product 等,一個子類 SampleFactoryImp 只能實現(xiàn)一套系列產(chǎn)品方案的生產(chǎn),如果還需要另外一套系統(tǒng)產(chǎn)品方案,就可能需要另外一個子類 SampleFactoryImpTwo 來實現(xiàn)。這樣,多個產(chǎn)品系列、多個工廠方法就形成了抽象工廠模式。

          前面已經(jīng)討論在 Jive 中設(shè)置了論壇統(tǒng)一入口,這個統(tǒng)一入口就是 ForumFactory ,以下是 ForumFactory 的主要代碼:

          public abstract class ForumFactory {

             private static Object initLock = new Object();

             private static String className = " com.Yasna.forum.database.DbForumFactory";

             private static ForumFactory factory = null;

          ??

             public static ForumFactory getInstance(Authorization authorization) {

               if (authorization == null) {

                 return null;

               }

               // 以下使用了 Singleton 單態(tài)模式,將在 2.3 節(jié)討論

               if (factory == null) {

                 synchronized(initLock) {

                   if (factory == null) {

                       ... // 從配置文件中獲得當(dāng)前 className

                     try {

                         // 動態(tài)裝載類

                         Class c = Class.forName(className);

                         factory = (ForumFactory)c.newInstance();

                     }

                     catch (Exception e) {

                         return null;

                     }

                   }

                 }

               }

               // 返回 proxy. 用來限制授權(quán)對 forum 的訪問

               return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));

             }

             // 創(chuàng)鍵產(chǎn)品接口 Forum 的具體對象實例

             public abstract Forum createForum(String name, String description)

             throws UnauthorizedException, ForumAlreadyExistsException;

          ?? // 創(chuàng)鍵產(chǎn)品接口 ForumThread 的具體對象實例

          public abstract ForumThread createThread(ForumMessage rootMessage)

          throws UnauthorizedException;

          // 創(chuàng)鍵產(chǎn)品接口 ForumMessage 的具體對象實例

          ?

          ?

          ??? public abstract ForumMessage createMessage();

             ....

          }

          ForumFactory 中提供了很多抽象方法如 createForum createThread createMessage() 等,它們是創(chuàng)建各自產(chǎn)品接口下的具體對象,這 3 個接口就是前面分析的基本業(yè)務(wù)對象 Forum 、 ForumThread ForumMessage ,這些創(chuàng)建方法在 ForumFactory 中卻不立即執(zhí)行,而是推遲到 ForumFactory 子類中實現(xiàn)。

          ForumFactory 的子類實現(xiàn)是 com.Yasna.forum.database.DbForumFactory ,這是一種數(shù)據(jù)庫實現(xiàn)方式。即在 DbForumFactory 中分別實現(xiàn)了在數(shù)據(jù)庫中 createForum createThread createMessage() 3 種方法,當(dāng)然也提供了動態(tài)擴展到另外一套系列產(chǎn)品的生產(chǎn)方案的可能。如果使用 XML 來實現(xiàn),那么可以編制一個 XmlForumFactory 的具體工廠子類來分別實現(xiàn) 3 種創(chuàng)建方法。

          因此, Jive 論壇在統(tǒng)一入口處使用了抽象工廠模式來動態(tài)地創(chuàng)建論壇中所需要的各種產(chǎn)品,如圖 3-4 所示。

          3-4? ForumFactory 抽象工廠模式圖

          3-4 , XmlForumFactory DbForumFactory 作為抽象工廠 ForumFactory 的兩個具體實現(xiàn) , Forum 、 ForumThread ForumMessage 分別作為 3 個系列抽象產(chǎn)品接口 , 依靠不同的工廠實現(xiàn)方式 , 會產(chǎn)生不同的產(chǎn)品對象。

          從抽象工廠模式去理解 Jive 論壇統(tǒng)一入口處,可以一步到位掌握了幾個類之間的大概關(guān)系。因為使用了抽象工廠模式這種通用的設(shè)計模式,可以方便源碼閱讀者快速地掌握整個系統(tǒng)的結(jié)構(gòu)和來龍去脈,圖 3-4 這張圖已經(jīng)初步展示了 Jive 的主要框架結(jié)構(gòu)。

          細(xì)心的讀者也許會發(fā)現(xiàn),在上面 ForumFactory 有一個 getInstance 比較令人費解,這將在 2.3 節(jié)進(jìn)行討論。


          2.3? 統(tǒng)一入口與單態(tài)模式

          在上面 ForumFactory getInstance 方法使用單態(tài)( SingleTon )模式。單態(tài)模式是保證一個類有且僅有一個對象實例,并提供一個訪問它的全局訪問點。

          前面曾提到 ForumFactory Jive 提供客戶端訪問數(shù)據(jù)庫系統(tǒng)的統(tǒng)一入口。為了保證所有的客戶端請求都要經(jīng)過這個 ForumFactory ,如果不使用單態(tài)模式,客戶端下列調(diào)用語句表示生成了 ForumFactory 實例:

          ForumFactory factory = new DbForumFactory();

          客戶端每發(fā)生一次請求都調(diào)用這條語句,這就會發(fā)生每次都生成不同 factory 對象實例,這顯然不符合設(shè)計要求,因此必須使用單態(tài)模式。

          一般在 Java 實現(xiàn)單態(tài)模式有幾種選擇,最常用而且安全的用法如下:

          public class Singleton {

             private Singleton(){}

             // 在自己內(nèi)部定義自己一個實例,是不是很奇怪

             // 注意這是 private ,只供內(nèi)部調(diào)用

             private static Singleton instance = new Singleton();

             // 這里提供了一個供外部訪問本 class 的靜態(tài)方法,可以直接訪問

             public static Singleton getInstance() {

               return instance;

             }

          }

          單態(tài)模式一共使用了兩條語句實現(xiàn):第一條直接生成自己的對象,第二條提供一個方法供外部調(diào)用這個對象,同時最好將構(gòu)造函數(shù)設(shè)置為 private ,以防止其他程序員直接使用 new Singleton 生成實例。

          還有一種 Java 單態(tài)模式實現(xiàn):

          public class Singleton {

             private Singleton(){}

             private static Singleton instance = null;

             public static synchronized Singleton getInstance() {

               if (instance==null)

                 instance new Singleton()

               return instance;

             }

          在上面代碼中,使用了判斷語句。如果 instance 為空,再進(jìn)行實例化,這成為 lazy initialization 。注意 getInstance() 方法的 synchronized ,這個 synchronized 很重要。如果沒有 synchronized ,那么使用 getInstance() 在第一次被訪問時有可能得到多個 Singleton 實例。

          關(guān)于 lazy initialization Singleton 有很多涉及 double-checked locking (DCL) 的討論,有興趣者可以進(jìn)一步研究。一般認(rèn)為第一種形式要更加安全些;但是后者可以用在類初始化時需要參數(shù)輸入的情況下。

          Jive ForumFactory 中采取了后者 lazy initialization 形式,這是為了能夠動態(tài)配置指定 ForumFactory 的具體子類。在 getInstance 中,從配置文件中獲得當(dāng)前工廠的具體實現(xiàn),如果需要啟動 XmlForumFactory ,就不必修改 ForumFactory 代碼,直接在配置文件中指定 className 的名字為 XmlForumFactory 。這樣通過下列動態(tài)裝載機制生成 ForumFactory 具體對象:

          Class c = Class.forName(className);

          factory = (ForumFactory)c.newInstance();

          這是利用 Java 的反射機制,可以通過動態(tài)指定 className 的數(shù)值而達(dá)到生成對象的方式。

          使用單態(tài)模式的目標(biāo)是為了控制對象的創(chuàng)建,單態(tài)模式經(jīng)常使用在控制資源的訪問上。例如數(shù)據(jù)庫連接或 Socket 連接等。單態(tài)模式可以控制在某個時刻只有一個線程訪問資源。由于 Java 中沒有全局變量的概念,因此使用單態(tài)模式有時可以起到這種作用,當(dāng)然需要注意是在一個 JVM 中。

          2.4? 訪問控制與代理模式

          仔細(xì)研究會發(fā)現(xiàn),在 ForumFactory getInstance 方法中最后的返回值有些奇怪。按照單態(tài)模式的概念應(yīng)該直接返回 factory 這個對象實例,但是卻返回了 ForumFactoryProxy 的一個實例,這實際上改變了單態(tài)模式的初衷。這樣客戶端每次通過調(diào)用 ForumFactory getInstance 返回的就不是 ForumFactory 的惟一實例,而是新的對象。之所以這樣做是為了訪問權(quán)限的控制,姑且不論這樣做的優(yōu)劣,先看看什么是代理模式。

          代理模式是屬于設(shè)計模式結(jié)構(gòu)型模式中一種,它是實際訪問對象的代理對象,或者影子對象,主要達(dá)到控制實際對象的訪問。這種控制的目的很多,例如提高性能等。即遠(yuǎn)程代理模式,這種模式將在以后章節(jié)討論。

          其中一個主要的控制目的是控制客戶端對實際對象的訪問權(quán)限。在 Jive 系統(tǒng)中,因為有角色權(quán)限的分別,對于 Forum 、 ForumThread FroumMessage 的訪問操作必須經(jīng)過權(quán)限機制驗證后才能進(jìn)行。

          ForumFactoryProxy 中的 createForum 方法為例,其實 ForumFactoryProxy 也是 FroumFactory 的一種工廠實現(xiàn),它的 createForum 具體實現(xiàn)如下:

          public Forum createForum(String name, String description)

          ??????????? throws UnauthorizedException, ForumAlreadyExistsException

          ??? {

          ??????? if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {

          ??????????? Forum newForum = factory.createForum(name, description);

          ??????????? return new ForumProxy(newForum, authorization, permissions);

          ??????? }

          ??????? else {

          ??????????? throw new UnauthorizedException();

          ??????? }

          }

          在這個方法中進(jìn)行了權(quán)限驗證,判斷是否屬于系統(tǒng)管理員。如果是,將直接從 DbForumFactory 對象 factory 的方法 createForum 中獲得一個新的 Forum 對象,然后再返回 Forum 的子類代理對象 ForumProxy 。因為在 Forum 中也還有很多屬性和操作方法,這些也需要進(jìn)行權(quán)限驗證。 ForumProxy ForumFactoryProxy 起到類似的作用。

          Jive 中有下列幾個代理類:

          ·????????? ForumFactoryProxy :客戶端和 DbForumFactory 之間的代理??蛻舳嗽L問 DbForumFactory 的任何方法都要先經(jīng)過 ForumFactoryProxy 相應(yīng)方法代理一次。以下意思相同。

          ·????????? ForumProxy :客戶端和 DbForum 之間的代理,研究 Forum 對象的每個方法,必須先看 ForumProxy 對象的方法。

          ·????????? ForumMessageProxy :客戶端和 DbForumMessage 之間的代理。

          ·????????? ForumThreadProxy :客戶端和 DbForumThread 之間的代理。

          User Group 也有相應(yīng)的代理類。

          由以上分析看出,每個數(shù)據(jù)對象都有一個代理。如果系統(tǒng)中數(shù)據(jù)對象非常多,依據(jù)這種一對一的代理關(guān)系,會有很多代理類,將使系統(tǒng)變得不是非常干凈,因此可以使用動態(tài)代理來代替這所有的代理類,具體實現(xiàn)將在以后章節(jié)討論。

          2.5? 批量分頁查詢與迭代模式

          迭代( Iterator )模式是提供一種順序訪問某個集合各個元素的方法,確保不暴露該集合的內(nèi)部表現(xiàn)。迭代模式應(yīng)用于對大量數(shù)據(jù)的訪問, Java Collection API Iterator 就是迭代模式的一種實現(xiàn)。

          在前面章節(jié)已經(jīng)討論過,用戶查詢大量數(shù)據(jù),從數(shù)據(jù)庫不應(yīng)該直接返回 ResultSet ,應(yīng)該是 Collection 。但是有一個問題,如果這個數(shù)據(jù)很大,需要分頁面顯示。如果一下子將所有頁面要顯示的數(shù)據(jù)都查詢出來放在 Collection ,會影響性能。而使用迭代模式則不必將全部集合都展現(xiàn)出來,只有遍歷到某個元素時才會查詢數(shù)據(jù)庫獲得這個元素的數(shù)據(jù)。

          以論壇中顯示帖子主題為例,在一個頁面中不可能顯示所有主題,只有分頁面顯示,如圖 3-5 所示。

          3-5 中一共分 15 頁來顯示所有論壇帖子,可以從顯示 Forum.jsp 中發(fā)現(xiàn)下列語句可以完成上述結(jié)果:

          ResultFilter filter = new ResultFilter();? // 設(shè)置結(jié)果過濾器

          ?? filter.setStartIndex(start);???????????? // 設(shè)置開始點

          ?? filter.setNumResults(range);????????? // 設(shè)置范圍

          ?? ForumThreadIterator threads = forum.threads(filter);? // 獲得迭代器

          ?? while(threads.hasNext){

          ?????? // 逐個顯示 threads 中帖子主題,輸出圖 3-5 中的每一行

          ?? }

          3-5? 分頁顯示所有帖子

          上述代碼中主要是從 Forum threads 方法獲得迭代器 ForumThreadIterator 的實例 , 依據(jù)前面代理模式中分析、研究 Forum 對象的方法 , 首先是看 ForumProxy 中對應(yīng)方法 然后再看 DbForum 中對應(yīng)方法的具體實現(xiàn)。在 ForumProxy threads 方法如下

          public ForumThreadIterator threads(ResultFilter resultFilter) {

          ???? ForumThreadIterator iterator = forum.threads(resultFilter);

          ????? return new ForumThreadIteratorProxy(iterator, authorization, permissions);

          }

          首先是調(diào)用了 DbForum 中具體的 threads 方法,再追蹤到 DbForum 中看看,它的 threads 方法代碼如下:

          public ForumThreadIterator threads(ResultFilter resultFilter) {

          // resultFilter 設(shè)置范圍要求獲得 SQL 查詢語句

          ?? String query = getThreadListSQL(resultFilter, false);?

          ?? // 獲得 resultFilter 設(shè)置范圍內(nèi)的所有 ThreadID 集合

          ?? long [] threadBlock = getThreadBlock(query.toString(), resultFilter.getStartIndex());

          ?? // 以下是計算查詢區(qū)域的開始點和終點

          ?? int startIndex = resultFilter.getStartIndex();

          ?? int endIndex;

          ?? // If number of results is set to inifinite, set endIndex to the total

          ? // number of threads in the forum.

          ?? if (resultFilter.getNumResults() == ResultFilter.NULL_INT) {

          ???? endIndex = (int)getThreadCount(resultFilter);

          ???}else {

          ???? endIndex = resultFilter.getNumResults() + startIndex;

          ?? }

          ?? return new ForumThreadBlockIterator(threadBlock, query.toString(),

          ?????????????? startIndex, endIndex, this.id, factory);

          }

          ResultFilter 是一個查詢結(jié)果類,可以對論壇主題 Thread 和帖子內(nèi)容 Message 進(jìn)行過濾或排序,這樣就可以根據(jù)用戶要求定制特殊的查詢范圍。如查詢某個用戶去年在這個論壇發(fā)表的所有帖子,那只要創(chuàng)建一個 ResultFilter 對象就可以代表這個查詢要求。

          在上面 threads 方法代碼中,第一步是先定制出相應(yīng)的動態(tài) SQL 查詢語句,然后使用這個查詢語句查詢數(shù)據(jù)庫,獲得查詢范圍內(nèi)所有的 ForumThread ID 集合,然后在這個 ID 集合中獲得當(dāng)前頁面的 ID 子集合,這是非常關(guān)鍵的一步。

          在這關(guān)鍵的一步中,有兩個重要的方法 getThreadListSQL getThreadBlock

          ·????????? GetThreadListSQL :獲得 SQL 查詢語句 query 的值,這個方法 Jive 實現(xiàn)起來顯得非常地瑣碎。

          ·????????? GetThreadBlock :獲得當(dāng)前頁面的 ID 子集合,那么如何確定 ID 子集合的開始位置呢?查看 getThreadBlock 方法代碼,可以發(fā)現(xiàn),它是使用最普遍的 ResultSet next() 方法來逐個跳躍到開始位置。

          上面代碼的 Threads 方法中最后返回的是 ForumThreadBlockIterator ,它是抽象類 ForumThreadIterator 的子類,而 ForumThreadIterator 繼承了 Collection Iterator ,以此聲明自己是一個迭代器, ForumMessageBlockIterator 實現(xiàn)的具體方法如下:

          public boolean hasNext();???? // 判斷是否有下一個元素

          public boolean hasPrevious()? // 判斷是否有前一個元素

          public Object next() throws java.util.NoSuchElementException? // 獲得下一個元素實例

          ForumThreadBlockIterator 中的 Block 是“頁”的意思,它的一個主要類變量 threadBlock 包含的是一個頁面中所有 ForumThread ID , next() 方法實際是對 threadBlock ForumThread 進(jìn)行遍歷,如果這個頁面全部遍歷完成,將再獲取下一頁( Block )數(shù)據(jù)。

          ForumThreadBlockIterator 重要方法 getElement 中實現(xiàn)了兩個功能:

          ·????????? 如果當(dāng)前遍歷指針超過當(dāng)前頁面,將使用 getThreadBlock 獲得下一個頁面的 ID 子集合;

          ·????????? 如果當(dāng)前遍歷指針在當(dāng)前頁面之內(nèi),根據(jù) ID 獲得完整的數(shù)據(jù)對象,實現(xiàn)輸出;

          ForumThreadBlockIterator getElement 方法代碼如下:

          private Object getElement(int index) {

          ?? if (index < 0) {??????? return null;??????? }

          ?? // 檢查所要獲得的 element 是否在本查詢范圍內(nèi)(當(dāng)前頁面內(nèi))

          ?? if (index < blockStart ||

          index >= blockStart + DbForum.THREAD_BLOCK_SIZE) {??

          ????? try {

          ????????? // 從緩沖中獲得 Forum 實例

          ????????? DbForum forum = factory.cacheManager.forumCache.get(forumID);

          ????????? // 獲得下一頁的內(nèi)容

          ????????? this.threadBlock = forum.getThreadBlock(query, index);

          ????????? this.blockID = index / DbForum.THREAD_BLOCK_SIZE;

          ????????? this.blockStart = blockID * DbForum.THREAD_BLOCK_SIZE;

          ????? } catch (ForumNotFoundException fnfe) {

          ?????????????? return null;

          ?????? }

          ???? }

          ???? Object element = null;

          ???? // 計算這個元素在當(dāng)前查詢范圍內(nèi)的相對位置

          ???? int relativeIndex = index % DbForum.THREAD_BLOCK_SIZE;

          ???? // Make sure index isn't too large

          ???? if (relativeIndex < threadBlock.length) {

          ??????? try {

          ??????????? // 從緩沖中獲得實際 thread 對象

          ??????????? element = factory.cacheManager.threadCache.get(

          ?????????? ?????????????threadBlock[relativeIndex]);

          ??????? } catch (ForumThreadNotFoundException tnfe) { }

          ???? }

          ???? return element;

          }

          ForumThreadBlockIterator 是真正實現(xiàn)分頁查詢的核心功能, ForumThreadBlockIterator 對象返回到客戶端的過程中,遭遇 ForumThreadIteratorProxy 的截獲,可以回頭看看 ForumProxy 中的 threads 方法,它最終返回給調(diào)用客戶端 Forum.jsp 的是 ForumThreadIteratorProxy 實例。

          ForumThreadIteratorProxy 也是迭代器 ForumThreadIterator 的一個子類,它的一個具體方法中:

          public Object next() {

          ? return new ForumThreadProxy((ForumThread)iterator.next(), authorization,

          ??????????? permissions);

          }

          這一句是返回一個 ForumThreadProxy 實例,返回就是一個 ForumThread 實例的代理。這里, Jive 使用代理模式實現(xiàn)訪問控制實現(xiàn)得不是很巧妙,似乎有代理到處“飛”的感覺,這是可以對之進(jìn)行改造的。

          從以上可以看出, Jive 在輸出如圖 3-5 所示的多頁查詢結(jié)果時,采取了下列步驟:

          1 )先查詢出符合查詢條件的所有對象元素的 ID 集合,注意不是所有對象元素,只是其 ID 的集合,這樣節(jié)約了大量內(nèi)存。

          2 )每個頁面視為一個 Block ,每當(dāng)進(jìn)入下一頁時,獲得下一個頁面的所有對象的 ID 集合。

          3 )輸出當(dāng)前頁面的所有對象時,首先從緩沖中獲取,如果緩沖中沒有,再根據(jù) ID 從數(shù)據(jù)庫中獲取完整的對象數(shù)據(jù)。

          上述實現(xiàn)方法完全基于即查即顯,相比于一般批量查詢做法:一次性獲得所有數(shù)據(jù),然后遍歷數(shù)據(jù)結(jié)果集 ResultSet Jive 這種批量查詢方式是一種比較理想的選擇。

          以上是 ForumThread 的批量顯示,有關(guān)帖子內(nèi)容 ForumMessage 也是采取類似做法。在每個 ForumThread 中可能有很多帖子內(nèi)容( ForumMessage 對象集合),也不能在一個頁面中全部顯示,所以也是使用迭代模式來實現(xiàn)的。顯示一個 Forum 主題下所有帖子內(nèi)容的功能由 ForumThread messages() 方法完成,檢查它的代理類 FroumThreadProxy 如何具體完成:

          public Iterator messages(ResultFilter resultFilter) {

          ?? Iterator iterator = thread.messages(resultFilter);

          ?? return new IteratorProxy(JiveGlobals.MESSAGE, iterator, authorization, permissions);

          }

          實現(xiàn)的原理基本相同,返回的都是一個 Iterator 代理類,在這些代理類中都是進(jìn)行用戶權(quán)限檢驗的。

          Jive 中也有關(guān)于一次性獲得所有數(shù)據(jù),然后遍歷 ResultSet 的做法。這種做法主要適合一次性查詢數(shù)據(jù)庫的所有數(shù)據(jù),例如查詢當(dāng)前所有論壇 Forum ,首先實現(xiàn) SQL 語句:

          SELECT forumID FROM jiveForum

          獲得所有 Forum forumID ,這段代碼位于 DbForumFactory.java forums 方法中,如 下:

          ? public Iterator forums() {

          ??? if (forums == null) {

          ????? LongList forumList = new LongList();

          ????? Connection con = null;

          ????? PreparedStatement pstmt = null;

          ????? try {

          ??????? con = ConnectionManager.getConnection();

          ??????? // GET_FORUMS 值是 SELECT forumID FROM jiveForum

          ??????? pstmt = con.prepareStatement(GET_FORUMS);

          ??????? ResultSet rs = pstmt.executeQuery();

          ??????? while (rs.next()) {

          ????????? forumList.add(rs.getLong(1));???????????????? // 將所有查詢 ID 結(jié)果放入 forumList

          ??????? }

          ????? }catch (SQLException sqle) {

          ??????? sqle.printStackTrace();

          ????? } finally {

          ??????? …

          ??? }

          ??? return new DatabaseObjectIterator(JiveGlobals.FORUM, forums, this);

          ? }

          forums 方法是返回一個 DatabaseObjectIterator ,這個 DatabaseObjectIterator 也是一個迭代器,但是實現(xiàn)原理要比 ForumThreadBlockIterator 簡單。它只提供了一個遍歷指針,在所有 ID 結(jié)果集中遍歷,然后也是通過 ID 獲得完整的數(shù)據(jù)對象。

          總之, Jive 中關(guān)于批量查詢有兩種實現(xiàn)方式:以 ForumThreadBlockIterator 為代表的實現(xiàn)方式適合在數(shù)據(jù)量巨大、需要多頁查詢時使用;而 DatabaseObjectIterator 則是推薦在一個頁面中顯示少量數(shù)據(jù)時使用。

          2.6? 過濾器與裝飾模式

          裝飾( Decorator )模式是動態(tài)給一個對象添加一些額外的職責(zé),或者說改變這個對象的一些行為。這就類似于使用油漆為某個東西刷上油漆,在原來的對象表面增加了一層外衣。

          在裝飾模式中,有兩個主要角色:一個是被刷油漆的對象( decoratee );另外一個是給 decoratee 刷油漆的對象( decorator )。這兩個對象都繼承同一個接口。

          首先舉一個簡單例子來說明什么是裝飾模式。

          先創(chuàng)建一個接口:

          public interface Work

          {

             public void insert();

          }

          這是一種打樁工作的抽象接口,動作 insert 表示插入,那么插入什么?下面這個實現(xiàn)表示方形木樁的插入:

          public class SquarePeg implements Work{

             public void insert(){

               System.out.println(" 方形樁插入 ");

             }

          }

          本來這樣也許就可以滿足打樁的工作需要,但是有可能土質(zhì)很硬,在插入方形樁之前先要打一個洞,那么又將如何實現(xiàn)?可以編制一個 Decorator 類,同樣繼承 Work 接口,但是在實現(xiàn) insert 方法時有些特別:

          public class Decorator implements Work{

             private Work work;

             // 額外增加的功能被打包在這個 List

             private ArrayList others = new ArrayList();

             public Decorator(Work work)

             {

               this.work=work;

               others.add(" 打洞 ");?? // 準(zhǔn)備好額外的功能

             }

             public void insert(){

               otherMethod();

               work.insert();

             }

             public void otherMethod()

             {

               ListIterator listIterator = others.listIterator();

               while (listIterator.hasNext())

               {

                 System.out.println(((String)(listIterator.next())) + " 正在進(jìn)行 ");

               }

          }

          }

          Decorator 的方法 insert 中先執(zhí)行 otherMethod() 方法,然后才實現(xiàn) SquarePeg insert 方法。油漆工 Decorator 給被油漆者 SquarePeg 添加了新的行為 —— 打洞。具體客戶端調(diào)用如下:

          Work squarePeg new SquarePeg();

          Work decorator = new Decorator(squarePeg);

          decorator.insert();

          本例中只添加了一個新的行為(打洞),如果還有很多類似的行為,那么使用裝飾模式的優(yōu)點就體現(xiàn)出來了。因為可以通過另外一個角度(如組織新的油漆工實現(xiàn)子類)來對這些行為進(jìn)行混合和匹配,這樣就不必為每個行為創(chuàng)建一個類,從而減少了系統(tǒng)的復(fù)雜性。

          使用裝飾模式可以避免在被油漆對象 decoratee 中包裝很多動態(tài)的,可能需要也可能不需要的功能,只要在系統(tǒng)真正運行時,通過油漆工 decorator 來檢查那些需要加載的功能,實行動態(tài)加載。

          Jive 論壇實現(xiàn)了信息過濾功能。例如可以將帖子內(nèi)容中的 HTML 語句過濾掉;可以將帖子內(nèi)容中 Java 代碼以特別格式顯示等。這些過濾功能有很多,在實際使用時不一定都需要,是由實際情況選擇的。例如有的論壇就不需要將帖子內(nèi)容的 HTML 語句過濾掉,選擇哪些過濾功能是由論壇管理者具體動態(tài)決定的。而且新的過濾功能可能隨時可以定制開發(fā)出來,如果試圖強行建立一種接口包含所有過濾行為,那么到時有新過濾功能加入時,還需要改變接口代碼,真是一種危險的行為。

          裝飾模式可以解決這種運行時需要動態(tài)增加功能的問題,且看看 Jive 是如何實現(xiàn)的。

          前面討論過,在 Jive 中,有主要幾個對象 ForumFactory 、 Forum 以及 ForumThread ForumMessage ,它們之間的關(guān)系如圖 3-2 所示。因此帖子內(nèi)容 ForumMessage 對象的獲得是從其上級 FroumThread 的方法 getMessage 中獲取,但是在實際代碼中, ForumThread 的方法 getMessage 委托 ForumFactory 來獲取 ForumMessage 對象??纯?/span> ForumThread 的子類 DbForumThread getMessage 代碼:

          public ForumMessage getMessage(long messageID)

          ???????? throws ForumMessageNotFoundException

          {

          ???? return factory.getMessage(messageID, this.id, forumID);

          }

          這是一種奇怪的委托,大概是因為需要考慮到過濾器功能有意為之吧。那就看看 ForumFactory 的具體實現(xiàn)子類 DbForumFactory getMessage 功能, getMessage 是將數(shù)據(jù)庫中的 ForumMessage 對象經(jīng)由過濾器過濾一遍后輸出(注:因為原來的 Jive getMessage 代碼考慮到可緩存或不可緩存的過濾,比較復(fù)雜,實際過濾功能都是可以緩存的,因此精簡如下)。

          protected ForumMessage getMessage(long messageID, long threadID, long forumID)

          ??????????? throws ForumMessageNotFoundException

          {

          ? ??????DbForumMessage message = cacheManager.messageCache.get(messageID);

          ??????? // Do a security check to make sure the message comes from the thread.

          ??????? if (message.threadID != threadID) {

          ??????????? throw new ForumMessageNotFoundException();

          ???? ???}

          ??????? ForumMessage filterMessage = null;

          ??????????? try {

          ? // 應(yīng)用全局過濾器

          ???? filterMessage = filterManager.applyFilters(message);

          ??????????????? Forum forum = getForum(forumID);???????????????

          ??????????????? // 應(yīng)用本論壇過濾器

          ??????????????? filterMessage = forum.getFilterManager().applyFilters(filterMessage);

          ??????????? }

          ??????????? catch (Exception e) { }

          ??????? return filterMessage;

          }

          上面代碼實際是裝飾模式的客戶端調(diào)用代碼, DbForumMessage 的實例 message 是被油漆者 decoratee 。通過 filterManager forum.getFilterManager() applyFilter 方法,將 message 實行了所有的過濾功能。這就類似前面示例的下列語句:

          Work decorator = new Decorator(squarePeg);

          forum.getFilterManager() 是從數(shù)據(jù)庫中獲取當(dāng)前配置的所有過濾器類。每個 Forum 都有一套自己的過濾器類,這是通過下列語句實現(xiàn)的:

          FilterManager filterManager =? new DbFilterManager();

          DbFilterManager 的類變量 ForumMessageFilter [] filters 中保存著所有的過濾器, applyFilters 方法實行過濾如下:

          public ForumMessage applyFilters(ForumMessage message) {

          ??? for (int i=0; i < filters.length; i++) {

          ? ???????if (filters[i] != null) {

          ??????????? message = filters[i].clone(message);

          ???????? }

          ?? ??}

          ??? ?return message;

          }

          ForumMessageFilter ForumMessage 的另外一個子類,被油漆者 DbForumMessage 通過油漆工 ForumMessageFilter 增加了一些新的行為和功能(過濾),如圖 3-6 所示。

          3-6? 裝飾模式

          這就組成了一個稍微復(fù)雜一點的裝飾模式。 HTMLFilter 實現(xiàn)了 HTML 代碼過濾功能,而 JavaCodeHighLighter 實現(xiàn)了 Java 代碼過濾功能, HTMLFilter 代碼如下:

          public class HTMLFilter extends ForumMessageFilter {

          ??? public ForumMessageFilter clone(ForumMessage message){

          ??????? HTMLFilter filter = new HTMLFilter();

          ??????? filter.message = message;

          ??????? return filter;

          ??? }

          ??? public boolean isCacheable() {

          ??????? return true;

          ??? }

          ??? public String getSubject() {

          ??????? return StringUtils.escapeHTMLTags(message.getSubject());

          ??? }

          ??? public String getBody() {

          ??????? return StringUtils.escapeHTMLTags(message.getBody());

          ??? }

          }

          HTMLFilter 中重載了 ForumMessage getSubject() getBody() 方法,實際是改變了這兩個原來的行為,這類似前面舉例的方法:

          public void insert(){

               otherMethod();

               work.insert();

          }

          這兩者都改變了被油漆者的行為。

          HTMLFilter 中還使用了原型( Prototype )模式,原型模式定義是: 用原型實例指定創(chuàng)建對象的種類,并且通過復(fù)制這些原型創(chuàng)建新的對象。按照這種定義, Java clone 技術(shù)應(yīng)該是原型模式的一個實現(xiàn)。

          HTMLFilter clone 方法實際就是在當(dāng)前 HTMLFilter 實例中再生成一個同樣的實例。這樣在處理多個并發(fā)請求時,不用通過同一個過濾器實例進(jìn)行處理,提高了性能。但是 HTMLFilter clone 方法是采取 new 方法來實現(xiàn),不如直接使用 Object native 方法速度快。

          因為在 DbFilterManager 中是根據(jù)配置使用類反射機制動態(tài)分別生成包括 HTMLFilter 在內(nèi)的過濾器實例。但是每種過濾器實例只有一個,為了使得大量用戶不必爭奪一個過濾器實例來實現(xiàn)過濾,就采取了克隆方式,這種實戰(zhàn)手法可以借鑒在自己的應(yīng)用系統(tǒng)中。

          2.7? 主題監(jiān)測與觀察者模式

          觀察者( Observer )模式是定義對象之間一對多的依賴關(guān)系,當(dāng)一個被觀察的對象發(fā)生改變時,所有依賴于它的對象都會得到通知并采取相應(yīng)行為。

          使用觀察者模式的優(yōu)點是將被觀察者和觀察者解耦,從而可以不影響被觀察者繼續(xù)自己的行為動作。觀察者模式適合應(yīng)用于一些“事件觸發(fā)”場合。

          Jive 中, 用戶也許會對某個主題感興趣,希望關(guān)于此主題發(fā)生的任何新的討論能通過電子郵件通知他,因此他訂閱監(jiān)視了這個主題。因為這個功能的實現(xiàn)會引入電子郵件的發(fā) 送。在前面章節(jié)已經(jīng)討論了電子郵件發(fā)送有可能因為網(wǎng)絡(luò)原因延遲,如果在有人回復(fù)這個主題時,立即進(jìn)行電子郵件發(fā)送,通知所有訂閱該主題的用戶。那么該用戶 可能等待很長時間得不到正常回應(yīng)。

          使用觀察者模式,可以通過觸發(fā)一個觀察者,由觀察者通過另外線程來實施郵件發(fā)送,而被觀察者發(fā)出觸發(fā)通知后,可以繼續(xù)自己原來的邏輯行為。

          看看 Jive WatchManager 類:

          public interface WatchManager {

          ??? // 正常監(jiān)察類型,用戶在這個主題更新后再次訪問時,會明顯地發(fā)現(xiàn)

          ??? public static final int NORMAL_WATCH = 0;

          ??? ?// 當(dāng)主題變化時,通過電子郵件通知用戶

          ??? public static final int EMAIL_NOTIFY_WATCH = 1;

          ??? // 設(shè)置一個主題被觀察的時間,默認(rèn)為 30

          ??? public void setDeleteDays(int deleteDays) throws UnauthorizedException;

          ??? public int getDeleteDays();

          ??? // 是否激活了 E-mail 提醒

          ? ??public boolean isEmailNotifyEnabled() throws UnauthorizedException;

          ??? public void setEmailNotifyEnabled(boolean enabled) throws UnauthorizedException;

          ??? // 保存 E-mail 的內(nèi)容

          ??? public String getEmailBody() throws UnauthorizedException;

          ??? public void setEmailBody(String body) throws UnauthorizedException;

          ??? // 保存 E-mail 的主題

          ??? public String getEmailSubject() throws UnauthorizedException;

          public void setEmailSubject(String subject) throws UnauthorizedException;

          ??? …

          ?

          ??? // 為某個主題創(chuàng)建一個觀察者

          ??? public void createWatch(User user, ForumThread thread, int watchType)

          ??????????? throws UnauthorizedException;

          ??? // 刪除某個主題的觀察者

          ??? public void deleteWatch(User user, ForumThread thread, int watchType)

          ??? // 得到一個主題的所有觀察者

          ??? public Iterator getWatchedForumThreads(User user, int watchType)

          ??????????? throws UnauthorizedException;

          ??? // 判斷一個用戶是否在觀察監(jiān)視該主題

          ??? public boolean isWatchedThread(User user, ForumThread thread, int watchType)

          ??????????? throws UnauthorizedException;

          ??? …

          }

          DbWatchManager WatchManager 的一個子類,通過數(shù)據(jù)庫保存著有關(guān)某個主題被哪些用戶監(jiān)視等數(shù)據(jù)資料。 WatchManager 對象是隨同 DbForumFactory() 一起生成的。

          DbWatchManager 中有一個 WatchManager 沒有的很重要的方法 —— 通知方法:

          protected void notifyWatches(ForumThread thread) {

          ???? //If watches are turned on.

          ??? if (!emailNotifyEnabled) {

          ??????????? return;

          ???? }

          ???? // 通知所有觀察這個主題的用戶

          ???? EmailWatchUpdateTask task = new EmailWatchUpdateTask(this, factory, thread);

          ???? TaskEngine.addTask(task);

          ?}

          這個方法用來觸發(fā)所有有關(guān)這個主題的監(jiān)視或訂閱用戶,以 E-mail 發(fā)送提醒他們。那么這個通知方法本身又是如何被觸發(fā)的?從功能上分析,應(yīng)該是在發(fā)表新帖子時觸發(fā)。

          DbForumThread addMessage 的最后一行有一句:

          factory.watchManager.notifyWatches(this);

          這其實是調(diào)用了 DbWatchManager notifyWatches 方法,因此確實是在增加新帖子時觸發(fā)了該帖子的所有觀察者。

          notifyWatches 方法中在執(zhí)行 E-mail 通知用戶時,使用了 TaskEngine 來執(zhí)行 E-mail 發(fā)送。 E-mailWatchUpdateTask 是一個線程類,而 TaskEngine 是線程任務(wù)管理器,專門按要求啟動如 E-mailWatchUpdateTask 這樣的任務(wù)線程。其實 TaskEngine 是一個簡單的線程池,它不斷通過查詢 Queue 是否有可運行的線程,如果有就直接運行線程。

          public class TaskEngine {

          ??? // 任務(wù)列表

          ??? private static LinkedList taskList = null;

          ??? // 工作數(shù)組

          ??? private static Thread[] workers = null;

          ??? private static Timer taskTimer = null;

          ??? private static Object lock = new Object();

          ?

          ??? static {

          ??????? // 根據(jù)配置文件初始化任務(wù)啟動時間

          ??????? taskTimer = new Timer(true);

          ??????? // 默認(rèn)使用 7 個線程來裝載啟動任務(wù)

          ??????? workers = new Thread[7];

          ??????? taskList = new LinkedList();

          ??????? for (int i=0; i<workers.length; i++) {

          ???????????? // TaskEngineWorker 是個簡單的線程類

          ??????????? TaskEngineWorker worker = new TaskEngineWorker();

          ??????????? workers[i] = new Thread(worker);

          ??????????? workers[i].setDaemon(true);

          ??????????? workers[i].start();???????? // 啟動 TaskEngineWorker 這個線程

          ??????? }

          ??? }

          ??? //TaskEngineWorker 內(nèi)部類

          ??? private static class TaskEngineWorker implements Runnable {

          ??????? private boolean done = false;

          ??????? public void run() {

          ??????????? while (!done) {

          ??????????????? // 運行 nextTask 方法

          ??????????????? nextTask().run();

          ??????????? }

          ????? ??}

          ??? }

          ??? // nextTask() 返回的是一個可運行線程,是任務(wù)列表 Queue 的一個讀取者

          ??? private static Runnable nextTask() {

          ??????? synchronized(lock) {

          ??????????? // 如果沒有任務(wù),就鎖定在這里

          ??????????? while (taskList.isEmpty()) {

          ??????????????? try {

          ??????????????????? lock.wait();????? ??// 等待解鎖

          ??????????????? } catch (InterruptedException ie) { }

          ??????????? }

          ??????????? // 從任務(wù)列表中取出第一個任務(wù)線程

          ??????????? return (Runnable)taskList.removeLast();

          ??????? }

          ??? }

          ??? public static void addTask(Runnable r) {

          ??????? addTask(r, Thread.NORM_PRIORITY);

          ??? }

          ??? // 這是任務(wù)列表 Queue 的生產(chǎn)者

          ??? public static void addTask(Runnable task, int priority) {

          ??????? synchronized(lock) {

          ??????????? taskList.addFirst(task);

          ??????????? // 提醒所有鎖在 lock 這里的線程可以運行了

          ??????????? // 這是線程的互相通知機制,可參考線程參考資料

          ??????????? lock.notifyAll();

          ??????? }

          ??? }

          ??? …

          }

          TaskEngine 中啟動設(shè)置了一個消息管道 Queue 和兩個線程。一個線程是負(fù)責(zé)向 Queue 里放入 Object ,可謂是消息的生產(chǎn)者;而另外一個線程負(fù)責(zé)從 Queue 中取出 Object ,如果 Queue 中沒有 Object ,那它就鎖定( Block )在那里,直到 Queue 中有 Object ,因為這些 Object 本身也是線程,因此它取出后就直接運行它們。

          這個 TaskEngine 建立的模型非常類似 JMS Java 消息系統(tǒng)),雖然它們功能類似,但不同的是: JMS 是一個分布式消息發(fā)布機制,可以在多臺服務(wù)器上運行,處理能力要強大得多。而 TaskEngine 由于基于線程基礎(chǔ),因此不能跨 JVM 實現(xiàn)??梢哉f TaskEngine 是一個微觀組件,而 JMS 則是一個宏觀架構(gòu)系統(tǒng)。 JMS 相關(guān)討論將在后面章節(jié)進(jìn)行。

          以上討論了 Jive 系統(tǒng)中觀察者模式的實現(xiàn), Jive 使用線程比較基礎(chǔ)的概念實現(xiàn)了觀察者模式,當(dāng)然有助于了解 J2EE 很多底層的基礎(chǔ)知識,整個 Web 容器的技術(shù)實現(xiàn)就是基于線程池原理建立的。

          Java JDK 則提供了比較方便的觀察者模式 API——java.util.Observable java.util.Observer ,它們的用戶非常簡單,只要被觀察者繼承 Observable ,然后使用下列語句設(shè)置觀察點:

          setChanged();

          notifyObservers(name); // 一旦執(zhí)行本代碼,就觸發(fā)觀察者了

          而觀察者只要實現(xiàn) Observer 接口,并實現(xiàn) update 方法,在 update 方法中將被觀察者觸發(fā)后傳來的 object 進(jìn)行處理。舉例如下:

          網(wǎng)上商店中商品價格可能發(fā)生變化,如果需要在價格變化時,首頁能夠自動顯示這些降價產(chǎn)品,那么使用觀察者模式將方便得多。首先,商品是一個被觀察者:

          public class product extends Observable{

             private float price;

             public float getPrice(){ return price;}

             public void setPrice(){

             this.price=price;

            // 商品價格發(fā)生變化,觸發(fā)觀察者

             setChanged();

             notifyObservers(new Float(price));

             }

             ...

          }

          價格觀察者實現(xiàn) observer 接口:

          public class PriceObserver implements Observer{

             private float price=0;

             public void update(Observable obj,Object arg){

               if (arg instanceof Float){

               price=((Float)arg).floatValue();

               System.out.println("PriceObserver :price changet to "+price);

               }

             }

          }

          這樣,一個簡單的觀察者模式就很容易地實現(xiàn)了。

          posted on 2006-08-31 12:26 Alex 閱讀(767) 評論(0)  編輯  收藏 所屬分類: java
          主站蜘蛛池模板: 高雄县| 云阳县| 东城区| 罗田县| 东光县| 商城县| 临汾市| 上林县| 阿坝县| 南宫市| 阜康市| 吴川市| 西华县| 永定县| 盘山县| 如东县| 耿马| 平塘县| 登封市| 山东省| 平遥县| 丹凤县| 阳春市| 清苑县| 科技| 崇左市| 夏津县| 离岛区| 繁昌县| 临桂县| 武威市| 阿拉善右旗| 隆昌县| 灵丘县| 溧阳市| 弋阳县| 卢氏县| 招远市| 信阳市| 获嘉县| 古田县|