`
hankzhousandy
  • 浏览: 8883 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Jive 源代码研究(转)

阅读更多

Jive 源代码研究

Jive 是一个开放的 Java 源代码项目。其目标是建设一个开放结构的,强壮的,易于扩展的基于 JSP 的论坛。在其设计目标的指导下,其结构设计得非常得好,融合了很多新的观念,比如 Design Pattern,可更换的 Skin,可插入 Plug 等等。详细解读其源代码对于理解这些新的设计上的概念是很有裨益的。如果你对 Design Pattern 和 Java 语言有一定的了解,但是还是会时常迷惑于其中的话,不妨研究研究 Jive 源代码,一定会对其中的很多概念有更深入的理解。这篇文章源于我的 Jive 源代码研究笔记,希望能够提纲挈领,带领大家进入到这个美好的世界。当然,如果没有时间仔细地看源代码的话,看看这篇文章,我想也是会有一些帮助的。

刘武东 (wdliu@chinaren.com),

2001 年 11 月 23 日

  • +内容

前言

Jive 是一个开放的 Java 源代码项目。其目标是建设一个开放结构的,强壮的,易于扩展的基于 JSP 的论坛。在其设计目标的指导下,其结构设计得非常得好,融合了很多新的观念,比如 Design Pattern,可更换的 Skin,可插入 Plug 等等。详细解读其源代码对于理解这些新的设计上的概念是很有裨益的。如果你对 Design Pattern 和 Java 语言有一定的了解,但是还是会时常迷惑于其中的话,不妨研究研究 Jive 源代码,一定会对其中的很多概念有更深入的理解。这篇文章源于我的 Jive 源代码研究笔记,希望能够提纲挈领,带领大家进入到这个美好的世界。当然,如果没有时间仔细地看源代码的话,看看这篇文章,我想也是会有一些帮助的。

再开始之前,需要指出的是,Jive 中对 Design Pattern 的应用,并没有拘礼与 GOF 书中所给出的实现方法,而是有许多变通的地方。一方面,我想是由于具体的实际需要,另一方面,我想这也是设计观念进化的结果吧。因而,这些变通的地方,将是我讲解的重点。

 

整体结构概叙

基于一个 OO 的设计原则:面向接口编程,而不是针对实现编程。Jive 在设计的时候,把其大部分的基本对象都设计为接口或者抽象类。在 Jive 中,基本的接口有 Forum,ForumMessage,ForumThread,Group,User,Authorization 和 Query。我们可以很容易的从这些接口的名字来知道他们的功用,下面的类图给出了这些类之间的一些静态关系:

图 1:Jive 整体关系
图 1:Jive 整体关系

你可能会有疑问,为什么会都是接口呢?这是基于扩展性考虑的。在 Jive 给出的实现中,所有的这些接口,Forum,ForumMessage,User 等等,都使用数据库来实现的,一条消息,或者一个用户对应于数据库中的一条消息 Jive 使用了 DbForum,DbForumMessage,DbUser 等类来实现这些接口,通过 JDBC 来操作数据库,使之作为论坛的底层支撑。

然而,有时候,或许我们并不想使用数据库,比如我们想只是使用文件系统来作为论坛的底层支撑,这时候,我们需要做的只是编码实现了 Forum 等等接口的诸如 FileFroum,FileForumMessage 等对象,然后嵌入 Jive 中即可,原有的任何代码都可以不用改变!!!这就是面向接口编程的威力了!

下面来看看具体的设计和编码。

 

AbstractFactory 模式和可扩展性

如果要实现较好的可扩展性,AbstractFactory 模式确实是一件利器。如上面所说,如果要创建的 Forum 接口的不同实现,而又不想更改代码的话,就需要用到抽象工厂了。再 Jive 中,AuthorizationFactory 类是一个抽象类,用来创建 Authorization 对象。这是一个抽象工厂,可以通过不同的子类来创建不同的 Authorization 对象。这个工厂的实现方法是:

在 AuthorizationFactory 中使用一个 private static 变量 factory,用来引用具体的抽象工厂的实例: 
private static AuthorizationFactory factory = null;

用一个 private static 的 String,来指明具体的抽象工厂的子类类名: 
private static String className ="com.coolservlets.forum.database.DbAuthorizationFactory";

然后是用一个 private static 的 loadAuthorizationFactory 方法来给这个 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 方法返回一个 Authorization 的过程中,先初始化工厂类 factory 变量,然后用 factory 的 createAuthorization 方法来创建:

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

不同的子类有不同的 createAuthorization 方法的实现。比如在 DbAuthorizationFactory 这个 AuthorizationFactory 的数据库实现子类中,createAuthorization 方法是这样实现的:

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

在这个类中,可以看到抽象类和具体的子类之间的关系,它们是如何协作的,又是如何划分抽象方法和非抽象方法的,这都是值得注意的地方。一般的,抽象方法需要子类来实现,而抽象类中的非抽象方法应该所有子类所能够共享的,或者可是说,是定义在抽象方法之上的较高层的方法。这确实是一个抽象工厂的好例子!虽然实现的方法已经和 GOF 中给出的实现相差较远了,但思想没变,这儿的实现,也确实是要巧妙的些。

还有就是静态方法的使用,使得这个类看起来有些 Singleton 的意味。这使得对于 AbstractFactory 的创建变得简单。

下面的类图给出了这个 AbstractFactory 的实现的总体情况:

图 2:AbstractFactory 模式的实现类图
图 2:AbstractFactory 模式的实现类图

在 AuthorizationFactory 中定义的其它方法,涉及到具体的如何创建 Authorization,都是作为 abstract 方法出现,具体实现留给子类来完成。

这样,在需要生成一个 Authorization 的时候,只需要调用 AuthorizationFactory 的静态方法 getAuthorization 就可以了,由子类实现了具体的细节。

其它的,如同上面讲到的,在创建 Forum 的时候用的 ForumFactory,具有同上面一样的实现,这就是模式之所以称为模式的所在了。

 

Proxy 模式和权限控制

Proxy 模式的功能有很多,比如远程代理,用来给远程对象提供一个本地代表;虚代理,用来为创建开大开销的对象提供缓冲,等等。在 Jive 中使用的是保护代理,为被保护的对象提供权限控制。

我们都知道在一个论坛中,权限的控制是必须的,否则论坛就很可能会被搞得一团糟。Jive 中引入 Proxy 对象,Authorization 接口以及权限描叙属类来提供对论坛的保护。

以 ForumFactory 为例,一个额外的 ForumFactoryProxy 来处理权限认证的工作,它为某一个 ForumFactory 提供了一个代理,保证只有授权的用户才能够存取 ForumFactory 的某些操作。实际上 ForumFactory 在这儿不仅仅只是一个生成 Forum 的类的,它更像是一个 Forum 的管理类。提供了添加,删除,枚举等等一系列的功能,而有些功能不是什么样的人都可以使用的,因而引入了另外的一个代理类来处理权限的问题。

当然,代理类需要继承 ForumFactory,以使方法签名一致: ForumFactoryProxy extends ForumFactory

在它的构造方法中,就提供了一个 ForumFactory 对象,这是需要被代理的对象;一个 Authorization 对象,提供用户信息;还有一个 ForumPermissions,提供认证信息:

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

一般的代理过程都是这样的,在访问某个方法之前,必须接受权限的检查,以 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(); 
        } 
 }

下面给出这个模式的类图:

图 3:Proxy 模式的类图
图 3:Proxy 模式的类图

这个模式的实现基本上和 GOF 中所给出的实现一致。在 Jive 中,几乎所有的接口,Forum,ForumMessage,ForumThread 等等,都会有一个相应的 Proxy 对象来进行权限控制。而在创建具体的对象的时候,都是用相应的 Proxy 对象来代替原有的对象返回的。例如在 ForumFactory 的 getInstance() 方法中需要返回一个 Forum 的时候,Jive 是这样做的:

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

因而,所有被创建的对象实际上都是 Proxy 对象,抽象工厂保证了没有权限验证的对象根本不会客户所得到,它们只会在 Proxy 的内部扮演角色,而永远不会被外部对象所存取,这样,就从根本上保证了论坛的安全。

 

Decorator 模式和过滤器

一般的在 OO 设计中,而外功能的添加是通过继承来实现的,但是继承有的时候不够灵活,而且当功能的组合很多的时候,继承的子类就会成几何级数增长,使得类多的难以控制。正是基于这样的考虑,Decorator 模式得以诞生。

Decorator 模式相当于封装了某个特定的操作,当某个对象需要这个操作的时候,加上这个 Decorator 即可。并且,多个 Decorator 还可以组合,以提供更多的功能。

在 Jive 中,Decorator 模式应用在一些过滤器(Filter)中。Filter 提供对 ForumMessage 对象内容的重新构造。比如,当一个 ForumMessage 对象流过一个名为 FilterCodeHighlight 的过滤器后,存在于消息中的所有 Java 源代码文本,会被重新构造为具有语法高亮显示的消息。在比如,当经过了语法高亮修饰的消息再流过一个名为 FilterHtml 的过滤器后,消息中的 HTML 片断会被注释可以在 HTML 内部显示文本,这样就防止了用户输入了 HTML 控制标签后,使得页面显示不正常的问题。

Jive 中,所有的过滤器继承于一个抽象类 ForumMessageFilter,而 ForumMessageFilter 又实现了 ForumMessage 接口。也就是说,每一个过滤器实际上也是一个 ForumMessage 对象。

ForumMessageFilter 中还封装一个 ForumMessage 对象。进行过滤的方法很简单,使用的是 getBody(),比如在 FilterCodeHighlight 这个类中:

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

highlightCode 是一个 private 方法,实施具体的过滤的细节。getBody() 方法实际上是定义在 ForumMessage 接口中的,当调用过滤器的 getBody() 方法时,就能够得到结构重整后的 ForumMessage 对象了。这个对象可以被其他客户引用,也可以在传递给另外的过滤器,实施进一步的操作。

在实现一个具体的消息的过滤的时候,在 Forum 中有 addForumMessageFilter(),applyFilters() 方法,用来实现对过滤器的应用。

对一个 Forum,使用 addForumMessageFilter() 方法添加一个 Filter 的时候,并没有指定一个具体的 Message,而只是一个规则(Filter 中封装了过滤规则),然后 applyFilter() 方法中,实施这些规则:

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

过滤器的 clone() 方法,为过滤器复制消息体。这个方法的使用,分离了在过滤器中对于消息体和过滤规则的初始化过程,这也是一个值得借鉴的技巧!

下面给出 Decorator 模式的类图 :

图 4:Decorator 模式的类图
图 4:Decorator 模式的类图

我们可以看到 Decorator 模式实际上和 Proxy 模式是很相近的,但是它们代表两个不同的功能含义。Proxy 模式提供一个对象的控制,而 Decorator 模式则是为对象提供额外的功能。

 

Iterator 模式和论坛的浏览

Iterator 模式用来分离数据结构和遍历算法,降低两者之间的耦合度,以使得同一个数据结构用不同的算法遍历时,仍能够具有相同的接口,另一方面,Iterator 模式使得当改换遍历算法后,不需要更改程序的代码。

在 Java 的 JDK 中本身就定义有一个 Iterator 接口,在 Iterator 接口中仅仅定义了三个方法,hasNext() 判断是否遍历完最后一个元素,next() 方法返回要遍历的数据结构中一个对象,remove() 则删除当前对象。Jive 中使用 IteratorProxy 抽象类继承了这一接口。这儿 Proxy 的含义和上面一样,也就是说,这个 IteratorProxy 出了会实现 Iterator 的遍历功能外,还会有代理权限控制的功能。

对于论坛中的基本对象 Forum,ForumThread,ForumMessage,Group,User 都有相应的遍历器。比如对应于 Forum 接口有 ForumIteratorProxy 对象。这个 ForumIteratorProxy 遍历器就相当于一个封装了一系列 Forum 对象的集合类,通过定义好的接口 hasNext() 和 next() 可以方便的遍历这个集合,而并不需要知道是如何遍历这个集合的。遍历的算法可能很简单,也可能很复杂,但是对于外部的客户而言,这并没有任何的区别。

而对于论坛中具体的遍历方法,这取决于具体的实现,在 Jive 中给出的是数据库的实现。

我们就以 MessageIteratorProxy 为例,来讲解 Iterator 模式的用法。

DbThreadIterator 对象实现了 Iterator 接口,是对于一个 Thread 中所有 Message 的遍历器,我们来看看它是如何实现的。

hasNext() 判断在这个 Thread 中是不是还有下一条 Message:

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

next() 方法从数据库中取出与在这个 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; 
    }

这样,通过对数据库的操作,DbThreadIterator 实现了对一个 Thread 中所有 Message 遍历的方法。

再 ForumThread 接口中有 messages() 方法,返回在这个 Thread 中的所有 Message 的一个遍历器(Iterator),实际上也就是返回了一个 Message 的集合: 
public Iterator messages();

在 DbForumThread 中实现了这个方法: 
public Iterator messages() {return new DbThreadIterator(this);}

从 DbForumThread 的 messages() 方法中所返回的就是这个 Thread 中所有 Message 的一个遍历器,通过这个遍历器,我们就可以访问 Thread 中的所有的 Message 了。当然,事情还没有完,由于权限的问题,我们还需要构造这个遍历器的 Proxy 对象,然后通过这个 Proxy 对象来访问遍历器。

下面的类图给出了在 Jive 中 Iterator 模式的实现方法:

图 5:Jive 中 Iterator 模式的实现
图 5:Jive 中 Iterator 模式的实现

在 Jive 中,因为在一个 Thread 之下,Message 是按树形结构组织的,因而,当需要层级表示一个 Thread 中的 Message 之间的关系的时候,仅仅用上面讲到的线性的 Iterator 是不够的。这时候,对 Iterator 的概念进行推广,就引入了 TreeWalker 接口。

顾名思义,TreeWalker 提供了遍历一个树和存取树上节点的方法:

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 的简单推广,并没有 Iterator 应用的那么广泛,而且,也可以很容易的在 TreeWalker 上面在套一层 Iterator 的借口,让它在某些情况下行使 Iterator 的职责。这儿就不再多讨论了。

再此,Jive 设计中所有涉及到的设计模式的地方,基本上都讲完了,看完了之后,是不是对设计模式有了更进一步的了解了呢?

下一部分的内容,将会涉及到具体的编码,深入到 JSP 的内部,我们将会看到 Jive 中是如何实现可更换的 Skin 的,还会涉及 Tag Library 的一些内容。好了,这次就到这儿了。下次再见。

参考资料

分享到:
评论

相关推荐

    jive2.1.1源代码,开源最彻底,拷贝到myeclipse直接可以跑起来

    jive2.1.1可以说是java进阶必须学习的一个...下载后只需自己稍微配置下初始化文件就可以,我敢保证网上基本上很难找到下载下来就可以跑的jive2.1.1源代码啦,好东西应该分享,希望大家自己研究,刚自己技术更上一层楼

    Jive源代码研究

    Jive是一个开放的Java源代码项目。其目标是建设一个开放结构的,...这篇文章源于我的Jive源代码研究笔记,希望能够提纲挈领,带领大家进入到这个美好的世界。当然,如果没有时间仔细地看源代码的话,看看这篇文章,我

    jive 2.5 源代码

    jive 2.5 源代码,里面包含有如何安装jive.

    jive源代码、设计模式学习

    jive学习源码学习,前一阵下载别人的不能用,传一个好使的.希望对奋斗在IT前线的同志们能有所帮助...系统设计,设计模式,数据库设计,权限设计,缓存系统,源代码研究,扩展功能,国际化支持,全文搜索,安全认证……

    Jive Forum 系统完整分析

    NULL 博文链接:https://tkiicpp.iteye.com/blog/556707

    jive.chm

    系统设计 1 jive设计思路 2 jive的工作内幕 3 Jive源代码研究 4 Jive中的设计模式 5 jive学习笔记 &lt;br&gt; 设计模式 1 大道至简-Java之23种模式一点就通 2 设计模式...

    Jive资料集

    系统设计 1 jive设计思路 2 jive的工作内幕 3 Jive源代码研究 4 Jive中的设计模式 5 jive学习笔记 &lt;br&gt; &lt;br&gt; 数据库设计 1 Jive Forums数据库说明(英文) 2 Jive KB...

    Jive论坛(J道版)

    学习Jive源程序,可以更好的理解和应用设计模式,本人推荐学习Java必须读懂两套源代码,第一个就是Jive 第二个是Sun的宠物店Pet Store. Jive论坛的优点是由于采用缓冲机制,可以承受巨大访问量,同时能保持...

    ciGen:ciGen 是一个简短的 C++ 代码,用于生成有凝聚力的界面元素。-开源

    该代码由 VP Nguyen(谷歌学者链接:http://scholar.google.com/citations?user=jCqqCAoAAAAJ&hl=en)在代尔夫特理工大学时编写,该算法在 VP Nguyen 的文章中有所描述,“An open生成零厚度内聚界面元素的源程序”...

    二十三种设计模式【PDF版】

    部代码研究时,这点尤其明显,因此,如果你不具备设计模式的基础知识(GoF 的设计模式),你很难快速的理解 J2EE。不能理解 J2EE,如何能灵活应用? 3.J2EE 只是适合企业计算应用的框架软件,但是 GoF 的设计模式几乎...

Global site tag (gtag.js) - Google Analytics