programmer's home, welcome here!

          technical issues and my life

          常用鏈接

          統(tǒng)計(jì)

          最新評(píng)論

          Jive 源碼研究與設(shè)計(jì)模式-轉(zhuǎn)

          轉(zhuǎn)自 IBM 劉武東,謝謝作者的辛勤勞動(dòng)!

          前言

          Jive是一個(gè)開(kāi)放的Java源代碼項(xiàng)目。其目標(biāo)是建設(shè)一個(gè)開(kāi)放結(jié)構(gòu)的,強(qiáng)壯的,易于擴(kuò)展的基于JSP的論壇。在其設(shè)計(jì)目標(biāo)的指導(dǎo)下,其結(jié)構(gòu)設(shè)計(jì)得非常得好,融合了很多新的觀念,比如Design Pattern,可更換的Skin,可插入Plug等等。詳細(xì)解讀其源代碼對(duì)于理解這些新的設(shè)計(jì)上的概念是很有裨益的。如果你對(duì)Design Pattern和Java語(yǔ)言有一定的了解,但是還是會(huì)時(shí)常迷惑于其中的話,不妨研究研究Jive源代碼,一定會(huì)對(duì)其中的很多概念有更深入的理解。這篇文章源于我的Jive源代碼研究筆記,希望能夠提綱挈領(lǐng),帶領(lǐng)大家進(jìn)入到這個(gè)美好的世界。當(dāng)然,如果沒(méi)有時(shí)間仔細(xì)地看源代碼的話,看看這篇文章,我想也是會(huì)有一些幫助的。

          再開(kāi)始之前,需要指出的是,Jive中對(duì)Design Pattern的應(yīng)用,并沒(méi)有拘禮與GOF書(shū)中所給出的實(shí)現(xiàn)方法,而是有許多變通的地方。一方面,我想是由于具體的實(shí)際需要,另一方面,我想這也是設(shè)計(jì)觀念進(jìn)化的結(jié)果吧。因而,這些變通的地方,將是我講解的重點(diǎn)。


          整體結(jié)構(gòu)概敘

          基于一個(gè)OO的設(shè)計(jì)原則:面向接口編程,而不是針對(duì)實(shí)現(xiàn)編程。Jive在設(shè)計(jì)的時(shí)候,把其大部分的基本對(duì)象都設(shè)計(jì)為接口或者抽象類。在Jive中,基本的接口有Forum,F(xiàn)orumMessage,F(xiàn)orumThread,Group,User,Authorization和Query。我們可以很容易的從這些接口的名字來(lái)知道他們的功用,下面的類圖給出了這些類之間的一些靜態(tài)關(guān)系:



          圖1:Jive整體關(guān)系
          圖1:Jive整體關(guān)系

          你可能會(huì)有疑問(wèn),為什么會(huì)都是接口呢?這是基于擴(kuò)展性考慮的。在Jive給出的實(shí)現(xiàn)中,所有的這些接口,F(xiàn)orum,F(xiàn)orumMessage,User等等,都使用數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)的,一條消息,或者一個(gè)用戶對(duì)應(yīng)于數(shù)據(jù)庫(kù)中的一條消息Jive使用了DbForum,DbForumMessage,DbUser等類來(lái)實(shí)現(xiàn)這些接口,通過(guò)JDBC來(lái)操作數(shù)據(jù)庫(kù),使之作為論壇的底層支撐。

          然而,有時(shí)候,或許我們并不想使用數(shù)據(jù)庫(kù),比如我們想只是使用文件系統(tǒng)來(lái)作為論壇的底層支撐,這時(shí)候,我們需要做的只是編碼實(shí)現(xiàn)了Forum等等接口的諸如FileFroum,F(xiàn)ileForumMessage等對(duì)象,然后嵌入Jive中即可,原有的任何代碼都可以不用改變?。。∵@就是面向接口編程的威力了!

          下面來(lái)看看具體的設(shè)計(jì)和編碼。

          AbstractFactory模式和可擴(kuò)展性

          如果要實(shí)現(xiàn)較好的可擴(kuò)展性,AbstractFactory模式確實(shí)是一件利器。如上面所說(shuō),如果要?jiǎng)?chuàng)建的Forum接口的不同實(shí)現(xiàn),而又不想更改代碼的話,就需要用到抽象工廠了。再Jive中,AuthorizationFactory類是一個(gè)抽象類,用來(lái)創(chuàng)建Authorization對(duì)象。這是一個(gè)抽象工廠,可以通過(guò)不同的子類來(lái)創(chuàng)建不同的Authorization對(duì)象。這個(gè)工廠的實(shí)現(xiàn)方法是:

          在AuthorizationFactory中使用一個(gè)private static變量factory,用來(lái)引用具體的抽象工廠的實(shí)例:
          private static AuthorizationFactory factory = null;

          用一個(gè)private static的String,來(lái)指明具體的抽象工廠的子類類名:
          private static String className ="com.coolservlets.forum.database.DbAuthorizationFactory";

          然后是用一個(gè)private static的loadAuthorizationFactory方法來(lái)給這個(gè)factory變量賦值,生成具體的抽象工廠類:

             private static void loadAuthorizationFactory() {
                      if (factory == null) {
                      synchronized(className) {
                      if (factory == null) {
                      String classNameProp = PropertyManager.getProperty(
                      "AuthorizationFactory.className"
                      );
                      if (classNameProp != null) {
                      className = classNameProp;
                      }
                      try {
                      Class c = Class.forName(className);
                      factory = (AuthorizationFactory)c.newInstance();
                      }
                      catch (Exception e) {
                      System.err.println("Exception loading class: " + e);
                      e.printStackTrace();
                      }
                      }
                      }
                      }
                      }

          在static的getAuthorization方法返回一個(gè)Authorization的過(guò)程中,先初始化工廠類factory變量,然后用factory的createAuthorization方法來(lái)創(chuàng)建:

             public static Authorization getAuthorization(String username,
                      String password) throws UnauthorizedException
                      {
                      loadAuthorizationFactory();
                      return factory.createAuthorization(username, password);
                      }

          不同的子類有不同的createAuthorization方法的實(shí)現(xiàn)。比如在DbAuthorizationFactory這個(gè)AuthorizationFactory的數(shù)據(jù)庫(kù)實(shí)現(xiàn)子類中,createAuthorization方法是這樣實(shí)現(xiàn)的:

             public Authorization createAuthorization(String username, String password)
                      throws UnauthorizedException
                      {
                      if (username == null || password == null) {
                      throw new UnauthorizedException();
                      }
                      password = StringUtils.hash(password);
                      int userID = 0;
                      Connection con = null;
                      PreparedStatement pstmt = null;
                      try {
                      con = DbConnectionManager.getConnection();
                      pstmt = con.prepareStatement(AUTHORIZE);
                      pstmt.setString(1, username);
                      pstmt.setString(2, password);
                      ResultSet rs = pstmt.executeQuery();
                      if (!rs.next()) {
                      throw new UnauthorizedException();
                      }
                      userID = rs.getInt(1);
                      }
                      catch( SQLException sqle ) {
                      System.err.println("Exception in DbAuthorizationFactory:" + sqle);
                      sqle.printStackTrace();
                      throw new UnauthorizedException();
                      }
                      finally {
                      try {  pstmt.close(); }
                      catch (Exception e) { e.printStackTrace(); }
                      try {  con.close();   }
                      catch (Exception e) { e.printStackTrace(); }
                      }
                      return new DbAuthorization(userID);
                      }

          在這個(gè)類中,可以看到抽象類和具體的子類之間的關(guān)系,它們是如何協(xié)作的,又是如何劃分抽象方法和非抽象方法的,這都是值得注意的地方。一般的,抽象方法需要子類來(lái)實(shí)現(xiàn),而抽象類中的非抽象方法應(yīng)該所有子類所能夠共享的,或者可是說(shuō),是定義在抽象方法之上的較高層的方法。這確實(shí)是一個(gè)抽象工廠的好例子!雖然實(shí)現(xiàn)的方法已經(jīng)和GOF中給出的實(shí)現(xiàn)相差較遠(yuǎn)了,但思想沒(méi)變,這兒的實(shí)現(xiàn),也確實(shí)是要巧妙的些。

          還有就是靜態(tài)方法的使用,使得這個(gè)類看起來(lái)有些Singleton的意味。這使得對(duì)于AbstractFactory的創(chuàng)建變得簡(jiǎn)單。

          下面的類圖給出了這個(gè)AbstractFactory的實(shí)現(xiàn)的總體情況:



          圖2:AbstractFactory模式的實(shí)現(xiàn)類圖
          圖2:AbstractFactory模式的實(shí)現(xiàn)類圖

          在AuthorizationFactory中定義的其它方法,涉及到具體的如何創(chuàng)建Authorization,都是作為abstract方法出現(xiàn),具體實(shí)現(xiàn)留給子類來(lái)完成。

          這樣,在需要生成一個(gè)Authorization的時(shí)候,只需要調(diào)用AuthorizationFactory的靜態(tài)方法getAuthorization就可以了,由子類實(shí)現(xiàn)了具體的細(xì)節(jié)。

          其它的,如同上面講到的,在創(chuàng)建Forum的時(shí)候用的ForumFactory,具有同上面一樣的實(shí)現(xiàn),這就是模式之所以稱為模式的所在了。





          回頁(yè)首


          Proxy模式和權(quán)限控制

          Proxy模式的功能有很多,比如遠(yuǎn)程代理,用來(lái)給遠(yuǎn)程對(duì)象提供一個(gè)本地代表;虛代理,用來(lái)為創(chuàng)建開(kāi)大開(kāi)銷的對(duì)象提供緩沖,等等。在Jive中使用的是保護(hù)代理,為被保護(hù)的對(duì)象提供權(quán)限控制。

          我們都知道在一個(gè)論壇中,權(quán)限的控制是必須的,否則論壇就很可能會(huì)被搞得一團(tuán)糟。Jive中引入Proxy對(duì)象,Authorization接口以及權(quán)限描敘屬類來(lái)提供對(duì)論壇的保護(hù)。

          以ForumFactory為例,一個(gè)額外的ForumFactoryProxy來(lái)處理權(quán)限認(rèn)證的工作,它為某一個(gè)ForumFactory提供了一個(gè)代理,保證只有授權(quán)的用戶才能夠存取ForumFactory的某些操作。實(shí)際上ForumFactory在這兒不僅僅只是一個(gè)生成Forum的類的,它更像是一個(gè)Forum的管理類。提供了添加,刪除,枚舉等等一系列的功能,而有些功能不是什么樣的人都可以使用的,因而引入了另外的一個(gè)代理類來(lái)處理權(quán)限的問(wèn)題。

          當(dāng)然,代理類需要繼承ForumFactory,以使方法簽名一致: ForumFactoryProxy extends ForumFactory

          在它的構(gòu)造方法中,就提供了一個(gè)ForumFactory對(duì)象,這是需要被代理的對(duì)象;一個(gè)Authorization對(duì)象,提供用戶信息;還有一個(gè)ForumPermissions,提供認(rèn)證信息:

             public ForumFactoryProxy(ForumFactory factory, Authorization authorization,
                      ForumPermissions permissions)
                      {
                      this.factory = factory;
                      this.authorization = authorization;
                      this.permissions = permissions;
                      }

          一般的代理過(guò)程都是這樣的,在訪問(wèn)某個(gè)方法之前,必須接受權(quán)限的檢查,以createForum為例:

             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();
                      }
                      }

          下面給出這個(gè)模式的類圖:



          圖3:Proxy模式的類圖
          圖3:Proxy模式的類圖

          這個(gè)模式的實(shí)現(xiàn)基本上和GOF中所給出的實(shí)現(xiàn)一致。在Jive中,幾乎所有的接口,F(xiàn)orum,F(xiàn)orumMessage,F(xiàn)orumThread等等,都會(huì)有一個(gè)相應(yīng)的Proxy對(duì)象來(lái)進(jìn)行權(quán)限控制。而在創(chuàng)建具體的對(duì)象的時(shí)候,都是用相應(yīng)的Proxy對(duì)象來(lái)代替原有的對(duì)象返回的。例如在ForumFactory的getInstance()方法中需要返回一個(gè)Forum的時(shí)候,Jive是這樣做的:

          public static ForumFactory getInstance(Authorization authorization) {
                      ......
                      ForumFactoryProxy proxy = new ForumFactoryProxy(factory,authorization, factory.getPermissions(authorization));
                      return proxy;
                      }

          因而,所有被創(chuàng)建的對(duì)象實(shí)際上都是Proxy對(duì)象,抽象工廠保證了沒(méi)有權(quán)限驗(yàn)證的對(duì)象根本不會(huì)客戶所得到,它們只會(huì)在Proxy的內(nèi)部扮演角色,而永遠(yuǎn)不會(huì)被外部對(duì)象所存取,這樣,就從根本上保證了論壇的安全。





          回頁(yè)首


          Decorator模式和過(guò)濾器

          一般的在OO設(shè)計(jì)中,而外功能的添加是通過(guò)繼承來(lái)實(shí)現(xiàn)的,但是繼承有的時(shí)候不夠靈活,而且當(dāng)功能的組合很多的時(shí)候,繼承的子類就會(huì)成幾何級(jí)數(shù)增長(zhǎng),使得類多的難以控制。正是基于這樣的考慮,Decorator模式得以誕生。

          Decorator模式相當(dāng)于封裝了某個(gè)特定的操作,當(dāng)某個(gè)對(duì)象需要這個(gè)操作的時(shí)候,加上這個(gè)Decorator即可。并且,多個(gè)Decorator還可以組合,以提供更多的功能。

          在Jive中,Decorator模式應(yīng)用在一些過(guò)濾器(Filter)中。Filter提供對(duì)ForumMessage對(duì)象內(nèi)容的重新構(gòu)造。比如,當(dāng)一個(gè)ForumMessage對(duì)象流過(guò)一個(gè)名為FilterCodeHighlight的過(guò)濾器后,存在于消息中的所有Java源代碼文本,會(huì)被重新構(gòu)造為具有語(yǔ)法高亮顯示的消息。在比如,當(dāng)經(jīng)過(guò)了語(yǔ)法高亮修飾的消息再流過(guò)一個(gè)名為FilterHtml的過(guò)濾器后,消息中的HTML片斷會(huì)被注釋可以在HTML內(nèi)部顯示文本,這樣就防止了用戶輸入了HTML控制標(biāo)簽后,使得頁(yè)面顯示不正常的問(wèn)題。

          Jive中,所有的過(guò)濾器繼承于一個(gè)抽象類ForumMessageFilter,而ForumMessageFilter又實(shí)現(xiàn)了ForumMessage接口。也就是說(shuō),每一個(gè)過(guò)濾器實(shí)際上也是一個(gè)ForumMessage對(duì)象。

          ForumMessageFilter中還封裝一個(gè)ForumMessage對(duì)象。進(jìn)行過(guò)濾的方法很簡(jiǎn)單,使用的是getBody(),比如在FilterCodeHighlight這個(gè)類中:

             public String getBody() {
                      return highlightCode(message.getBody());
                      }

          highlightCode是一個(gè)private方法,實(shí)施具體的過(guò)濾的細(xì)節(jié)。getBody()方法實(shí)際上是定義在ForumMessage接口中的,當(dāng)調(diào)用過(guò)濾器的getBody()方法時(shí),就能夠得到結(jié)構(gòu)重整后的ForumMessage對(duì)象了。這個(gè)對(duì)象可以被其他客戶引用,也可以在傳遞給另外的過(guò)濾器,實(shí)施進(jìn)一步的操作。

          在實(shí)現(xiàn)一個(gè)具體的消息的過(guò)濾的時(shí)候,在Forum中有addForumMessageFilter(),applyFilters()方法,用來(lái)實(shí)現(xiàn)對(duì)過(guò)濾器的應(yīng)用。

          對(duì)一個(gè)Forum,使用addForumMessageFilter()方法添加一個(gè)Filter的時(shí)候,并沒(méi)有指定一個(gè)具體的Message,而只是一個(gè)規(guī)則(Filter中封裝了過(guò)濾規(guī)則),然后applyFilter()方法中,實(shí)施這些規(guī)則:

             public ForumMessage applyFilters(ForumMessage message) {
                      //Loop through filters and apply them
                      for (int i=0; i < filters.length; i++) {
                      message = filters[i].clone(message);
                      }
                      return message;
                      }

          過(guò)濾器的clone()方法,為過(guò)濾器復(fù)制消息體。這個(gè)方法的使用,分離了在過(guò)濾器中對(duì)于消息體和過(guò)濾規(guī)則的初始化過(guò)程,這也是一個(gè)值得借鑒的技巧!

          下面給出Decorator模式的類圖:



          圖4:Decorator模式的類圖
          圖4:Decorator模式的類圖

          我們可以看到Decorator模式實(shí)際上和Proxy模式是很相近的,但是它們代表兩個(gè)不同的功能含義。Proxy模式提供一個(gè)對(duì)象的控制,而Decorator模式則是為對(duì)象提供額外的功能。

          Iterator模式和論壇的瀏覽erator模式用來(lái)分離數(shù)據(jù)結(jié)構(gòu)和遍歷算法,降低兩者之間的耦合度,以使得同一個(gè)數(shù)據(jù)結(jié)構(gòu)用不同的算法遍歷時(shí),仍能夠具有相同的接口,另一方面,Iterator模式使得當(dāng)改換遍歷算法后,不需要更改程序的代碼。

          在Java的JDK中本身就定義有一個(gè)Iterator接口,在Iterator接口中僅僅定義了三個(gè)方法,hasNext()判斷是否遍歷完最后一個(gè)元素,next()方法返回要遍歷的數(shù)據(jù)結(jié)構(gòu)中一個(gè)對(duì)象,remove()則刪除當(dāng)前對(duì)象。Jive中使用IteratorProxy抽象類繼承了這一接口。這兒Proxy的含義和上面一樣,也就是說(shuō),這個(gè)IteratorProxy出了會(huì)實(shí)現(xiàn)Iterator的遍歷功能外,還會(huì)有代理權(quán)限控制的功能。

          對(duì)于論壇中的基本對(duì)象Forum,F(xiàn)orumThread,F(xiàn)orumMessage,Group,User都有相應(yīng)的遍歷器。比如對(duì)應(yīng)于Forum接口有ForumIteratorProxy對(duì)象。這個(gè)ForumIteratorProxy遍歷器就相當(dāng)于一個(gè)封裝了一系列Forum對(duì)象的集合類,通過(guò)定義好的接口hasNext()和next()可以方便的遍歷這個(gè)集合,而并不需要知道是如何遍歷這個(gè)集合的。遍歷的算法可能很簡(jiǎn)單,也可能很復(fù)雜,但是對(duì)于外部的客戶而言,這并沒(méi)有任何的區(qū)別。

          而對(duì)于論壇中具體的遍歷方法,這取決于具體的實(shí)現(xiàn),在Jive中給出的是數(shù)據(jù)庫(kù)的實(shí)現(xiàn)。

          我們就以MessageIteratorProxy為例,來(lái)講解Iterator模式的用法。

          DbThreadIterator對(duì)象實(shí)現(xiàn)了Iterator接口,是對(duì)于一個(gè)Thread中所有Message的遍歷器,我們來(lái)看看它是如何實(shí)現(xiàn)的。

          hasNext()判斷在這個(gè)Thread中是不是還有下一條Message:

          public boolean hasNext() {
                      if (currentIndex+1 >= messages.length) {
                      return false;
                      }
                      return true;
                      }

          next()方法從數(shù)據(jù)庫(kù)中取出與在這個(gè)Thread中的下一條Message:

             public Object next() throws java.util.NoSuchElementException {
                      ForumMessage message = null;
                      if (nextMessage != null) {
                      message = nextMessage;
                      nextMessage = null;
                      }
                      else {
                      message = getNextMessage();
                      if (message == null) {
                      throw new java.util.NoSuchElementException();
                      }
                      }
                      return message;
                      }

          這樣,通過(guò)對(duì)數(shù)據(jù)庫(kù)的操作,DbThreadIterator實(shí)現(xiàn)了對(duì)一個(gè)Thread中所有Message遍歷的方法。

          再ForumThread接口中有messages()方法,返回在這個(gè)Thread中的所有Message的一個(gè)遍歷器(Iterator),實(shí)際上也就是返回了一個(gè)Message的集合:
          public Iterator messages();

          在DbForumThread中實(shí)現(xiàn)了這個(gè)方法:
          public Iterator messages() {return new DbThreadIterator(this);}

          從DbForumThread的messages()方法中所返回的就是這個(gè)Thread中所有Message的一個(gè)遍歷器,通過(guò)這個(gè)遍歷器,我們就可以訪問(wèn)Thread中的所有的Message了。當(dāng)然,事情還沒(méi)有完,由于權(quán)限的問(wèn)題,我們還需要構(gòu)造這個(gè)遍歷器的Proxy對(duì)象,然后通過(guò)這個(gè)Proxy對(duì)象來(lái)訪問(wèn)遍歷器。

          下面的類圖給出了在Jive中Iterator模式的實(shí)現(xiàn)方法:



          圖5:Jive中Iterator模式的實(shí)現(xiàn)
          圖5:Jive中Iterator模式的實(shí)現(xiàn)

          在Jive中,因?yàn)樵谝粋€(gè)Thread之下,Message是按樹(shù)形結(jié)構(gòu)組織的,因而,當(dāng)需要層級(jí)表示一個(gè)Thread中的Message之間的關(guān)系的時(shí)候,僅僅用上面講到的線性的Iterator是不夠的。這時(shí)候,對(duì)Iterator的概念進(jìn)行推廣,就引入了TreeWalker接口。

          顧名思義,TreeWalker提供了遍歷一個(gè)樹(shù)和存取樹(shù)上節(jié)點(diǎn)的方法:

          public interface TreeWalker {
                      public ForumMessage getRoot();
                      public ForumMessage getChild(ForumMessage parent, int index);
                      public int getChildCount(ForumMessage parent);
                      public int getRecursiveChildCount(ForumMessage parent);
                      public int getIndexOfChild(ForumMessage parent, ForumMessage child);
                      public boolean isLeaf(ForumMessage node);

          TreeWalker只是Iterator的簡(jiǎn)單推廣,并沒(méi)有Iterator應(yīng)用的那么廣泛,而且,也可以很容易的在TreeWalker上面在套一層Iterator的借口,讓它在某些情況下行使Iterator的職責(zé)。這兒就不再多討論了。

          再此,Jive設(shè)計(jì)中所有涉及到的設(shè)計(jì)模式的地方,基本上都講完了,看完了之后,是不是對(duì)設(shè)計(jì)模式有了更進(jìn)一步的了解了呢?

          下一部分的內(nèi)容,將會(huì)涉及到具體的編碼,深入到JSP的內(nèi)部,我們將會(huì)看到Jive中是如何實(shí)現(xiàn)可更換的Skin的,還會(huì)涉及Tag Library的一些內(nèi)容。好了,這次就到這兒了。下次再見(jiàn)。

          posted on 2007-04-14 12:46 crazy zerlot 閱讀(627) 評(píng)論(0)  編輯  收藏 所屬分類: Patterns Related


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 抚松县| 甘孜县| 南阳市| 玛沁县| 高邑县| 武山县| 曲靖市| 金坛市| 雷波县| 图木舒克市| 横山县| 庄河市| 化州市| 岚皋县| 循化| 凭祥市| 文山县| 乌拉特前旗| 松江区| 望奎县| 桦甸市| 策勒县| 武定县| 磐安县| SHOW| 阳西县| 桐庐县| 芷江| 蕉岭县| 怀柔区| 汕头市| 彰化县| 长顺县| 虹口区| 菏泽市| 乌鲁木齐县| 鹤山市| 武胜县| 堆龙德庆县| 和龙市| 十堰市|