Jive 中的设计模式

关于设计模式 , 这篇文章并不详细解释 , 只是结合 Jive 来看看设计模式在一个实际项目中的应用及其整体的设计思想 . 所以在读这篇文章前 , 假设您对设计模式有一个感性的认识 , 对其具体应用以及实现方法有些疑问 , 并渴望了解其思想 , 并使用过 Jive. 本文将一同来探讨这个问题 . 为什么选择 Jive 而不是选择一个新的例子重新开始呢 ? 有以下两个原因 : 1, 我们很多人对 bbs 这样一个事物比较熟悉 , 很清楚 bbs 所具有的一些基本功能 , 如果自己作为设计者来设计这样一个 web bbs, 会怎么想 , 再看看别人是怎么实现的 , 有对比才能明白自己设计上的缺点 , 看到别人的优点才能更快地进步 . 2, Jive 并不是非常地复杂 , 并且包括了一个完整的实现方案 , 从底层到高层 , 从后端到前端 , 都有很好的文档 , 这些都能更好地帮助我们理解它 .

这里我们所用的 Jive 的版本采用其开发者作为正式发布的 1.0 版 , 其最新版为 1.21, 对其结构作了少量改动 , 主要增加了 jsp tag 的支持 , 这种技术不属于我们的讨论范围 , 以后有机会可以共同学习 .

Jive 中所使用的设计模式 , 对设计模式的三种类型 -- 创建型 , 结构型 , 行为型 -- 都有涉及 , 这样也能比较全面地了解设计模式 . 我们先来自己设计一下 , 运用面向对象的思想 , 可以很容易知道 , 整个系统主要需要这几个对象 :

  1. Forum    --   一个讨论区 , 也就是一个版面 .
  2. Thread    --   一条线索 , 也就是有关同一个主题的所有的回文 .
  3. Message   --   一条消息 , 也就是一个用户发的一篇贴子 .( 以后我们就用"贴子"这个叫法 )
  4. User     --   一个用户 , 也就是讨论区的使用者 .

好了 , 我们需要的东西都在了 , 它们之间的关系十分复杂 , 怎么把它们组织地很符合我们的思路又能容易扩充呢 ? 我想大家都有自己的想法了 , "我能这么这么做","我可以这样这样设计", 我们一起来看看 Jive 是怎么做的 . 下面是其整体结构 :

                  |~~~~~~~~~~~~~~~~~~| 
                    |   Skin 设计者    | 
                    |__________________| 
                            | | 
                            | |  使用
                            \ / 
                    |~~~~~~~~~~~~~~~~~| 
                    | 各种对象的接口  | 
                    |_________________| 
                            | | 
                            | |  被实现
                            \ / 
                       |~~~~~~~~~~~~| 
                       |  权限控制  | 
                       |____________| 
                            | | 
                            | |  控制
                            \ / 
                |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 
                | 对数据库进行操作的各种对象  | 
                |_____________________________| 
                            | | 
                            | |  取连接
                            \ / 
                     |~~~~~~~~~~~~~~~~| 
                     |  数据库连接池  | 
                     |________________| 
                     ( 图 1)

下面是其类的大概的继承情况 :

                 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 
                 |          Interface  A             | 
                 |___________________________________| 
                         |                    | 
                         |    implements      | 
                         |                    | 
                 |~~~~~~~~~~~~~~~~~|          | 
                 |   Proxy   A     |          | 
                 |_________________|          | 
                                              | 
                                              | 
                                     |~~~~~~~~~~~~~~~~~~| 
                                     |    Database A    | 
                                     |__________________| 
                  ( 图 2)

好了看到这里 , 如果您对设计模式有了解的话 , 从上面所写的伪名字中 , 可以看到一些熟悉的东西 . 请让我做一些解释 . 上面的图表示的是类的继承关系 , A 代表上面所提到的四种对象 , Interface A 表示名为 A 的一个接口 , 相信大家对接口都不陌生 , 接口在 java 中有着重要的作用 . Proxy A 表示一个名为 ProxyA 的类 , 实现 A 接口 . Database A 表示名为 DbA 的一个类 , 实现 A 接口 . 但设计模式并没有从中体现出来 , 设计模式所要表现的是怎么样更好地组织对象之间的逻辑关系 , 怎么样才能更好地扩充现有的东西而不需要作很大的改动 , 而不仅仅是类的继承 .

还有一点需要说明的是 , 设计模式总的原则是针对接口编程 , 而不关心其具体实现 , 这样搭起来的是一个架子 , 还需要作许多具体的编程才能真正的完成系统 .

下面 , 我们就分别从设计模式的三种类型来看 Jive 使用了其中的哪些 .

创建型模式 (Creational Patterns)

这一类型的设计模式 , 所要表现的是对象的创建过程及和用户所使用的对象之间的关系 .

  1. Jive 中在 Forum 之上又加了一层 , ForumFactory, 来实现对 Forum 的一些控制 , 比如创建新的讨论区 , 删除一个讨论区等等 . 这个类实际上是整个系统的入口 ,jsp 中所做的一切都要从得到这个类的一个实例开始 . 它的一些子类和它的关系如下 :
                          |~~~~~~~~~~~~~~~~~| 
                            |  ForumFactory   | abstract 
                            |_________________| 
                               |           | 
                               |  extends  | 
                               |           | 
                  |~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~| 
                  | ForumFactoryProxy  | |  DbForumFactory | 
                  |____________________| |_________________| 
                    ( 图 3)

    我们来看一下得到一个 ForumFactory 实例的过程 :
    FactoryForum factory = ForumFactory.getInstance(aAuthorization); 就得到了 ForumFactory 的实例 , 这个最终用户 (skin 设计人员 ) 所使用的是它的子类 ForumFactoryProxy 的实例 , ( 其中涉及到另一个模式 , 后面将会提到 ), 但实际上真正在做实际工作的是 DbForumFactory 或者是一个指定的类的实例 , 相关代码如下 :
     From ForumFactory.java 
        
     private static String className = "com.coolservlets.forum.database.DbForumFaactory"; 
     // 系统缺省的 ForumFactory 的一个具体的子类 . 
     private static ForumFactory factory = null; 
     ForumFactory.getInstance() 
     String classNameProp = PropertyManager.getProperty("ForumFactory.className") 
     // 可以通过配制文件来选择其他的具体的子类 . 
     if (classNameProp != null) { 
        className = classNameProp; 
     } 
    
     try { 
        //Load the class and create an instance. 
        Class c = Class.forName(className); 
        factory = (ForumFactory)c.newInstance(); 
     } 
     catch (Exception e) { 
        System.err.println("Failed to load ForumFactory class "
            + className + ". Jive cannot function normally."); 
        e.printStackTrace(); 
        return null; 
     } 
    

    它使用的是 Abstract Factory ( 抽象工厂 ) 设计模式 . 给用户一个使用一系列相关对象的接口 , 而不需要指定其具体的类 . 也就是说 , skin 设计人员写的 jsp 中不应该出现 new DbForumFactory 之类的语句 . Jive 中 AuthorizationFactory 也使用了这个设计模式
  2. Jive 中有一个很不错的想法 , 就是对贴子的内容和标题可以进行过滤 , 比如过滤 html 过滤一些脏话 , 对附加的代码进行高亮显示 , 转换链接等等 . 如果我要实现这样的功能 , 有有下几种方法 : (1) 在 Message.getBody() getSubject() 中进行控制 , (2) 在 Thread 中得得 Message 后进行转换 . 还需要考虑的问题是这些过滤的操作必须能够很方便地添加删除 . 不不的目标所用的设计方法是不一样的 , Jive 是这样做的 : 以版面为主 , 把这些过滤器看作是鞍婷的属性 , 过滤器只对其所属的版面有效 , 所以 Jive 中使用了 (2), 这并不是主要的 , 重要要是这些过滤器该怎么来组织 . 我们先来看看需求 : 能动态添加删除 , 功能类似 , 贴子的显示示其具体怎么创建 , 如何表现无关 . 似乎目标只有一个 -- Prototype( 原型 ) 设计模式 . 看看 Jive 的具体实现 .
                                    |~~~~~~~~~~~~~~~~~~~~| 
                                      |    ForumMessage    | 
                                      |____________________| 
                                               | 
                                               | implements 
                                               | 
       |~~~~~~~~~~~~~~~~| Prototype   |~~~~~~~~~~~~~~~~~~~~~| 
       |  ForumThread   |-----------> |  ForumMessageFilter | 
       |----------------|             |---------------------| 
       | getMessage() o |             |     clone()         | 
       |______________|_|             |_____________________| 
                      |                 /             | 
       |~~~~~~~~~~~~~~~~|      |~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~| 
       | aFilter.clone()|      | HighlightCode | |    HTML     | 
       |________________|      |---------------| |-------------| ...... 
                               |  clone()  o   | |   clone() o | 
                               |___________|___| |___________|_| 
                                           |                 | 
                               |~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~| 
                               |  返回一个实例 | |  返回一个实例 | 
                               |_______________| |_______________| 
                               ( 图 4)

    上图作了少许的简化 . Jive 用的时候是把这些过滤器存在数据库中 , 可以动态设置 属性 , 比较方便 . 来看一些代码 :
     From: DbForumThread.java 
        public ForumMessage getMessage(int messageID) 
                throws ForumMessageNotFoundException 
        { 
            ForumMessage message = factory.getMessage(messageID); 
            //Apply filters to message. 
            message = forum.applyFilters(message); 
            // 通过 Forum 来实现 , 因为 Filter 是 Forum 的属性 , 
            //Thread 只能通过 Forum 的接口来访问 . 
            return message; 
        } 
     From: DbForum.java 
        public ForumMessage applyFilters(ForumMessage message) { 
            for (int i=0; i < filters.length; i++) { 
                message = filters[i].clone(message); 
            } 
            // 可能会有多个过滤器 , 依次来操作 . 
            return message; 
        }

结构型模式 (Structural Patterns)

这一类的模式关心类和对象之间怎么组织起来形成大的结构 . 主要使用继承来组织接口或实现 .

我们再接着思考一下 , 用户之间应该有所区别 , 有 Guest 用户 , 可以让他来看一看 , 但不能发贴子 , 正式用户可以发贴子 , 查看自己的个人信息 , 版面管理者 ( 称之为版主 ) 应该可以控制贴子 , 比如加上适当的标记 , 收入精华区 , 甚至删除贴子等等 , 而系统管理者应该具有更高的权限 , 比如开新的版面 , 删除用户等操作 . 怎么实现这个功能呢 ? 我们知道 , Jive 中所有实际的操作都是由 database 目录下的类所实现的 , 如果把权限控制加到数据库这一层的话 , 这一层不但臃肿 , 而且写好以后 , 如果要改的话 , 需要修改的地方很多 , 还容易出错 , 所以可以在这一层之上再加一层 , 单独进行权限控制 . 这样就把 "该不该做" 和 "怎么做" 分割开来 , 利于以后修改 . 其实这也是面象对象的一个思想 -- 一个对象不要负担太多的责任 . 这种方法在设计模式中称为 Proxy ( 代理 ) 模式 . 好比生产厂家和代理商的关系 . ( 当然 , 在 Jive 中这个比喻不太合适 ). Proxy 的目的就是给另一个对象提供一个代理来控制对它的访问 . Proxy 模式一直贯穿 Jive 的始终 , 几乎所涉及到的对象都需要 . 其结构如图 2 所示 . 从前面已经知道 , ForumFactory 是整个系统的开始 . 再来看看 ForumFactory 的代码 :

From ForumFactory.java 
 ForumFactory.getInstance() 的最后 : 
        ForumFactoryProxy proxy = new ForumFactoryProxy( 
                                    factory, 
                                    authorization, 
                                    factory.getPermissions(authorization) 
                                  ); 
        return proxy;

前面得到的 factory 是 DbForumFactory 的实例 , 这里把这个实例又用 ForumFactoryProxy 封装起来 . 最后返回一个 ForumFactoryProxy 的实例 . 也就是说 jsp skin 的设计者所用的 ForumFactory 实际上是 ForumFactoryProxy. 接着看看 ForumFactoryProxy 里发生了什么事 , 那一个小片段做例子 : 其构造函数中的 Factory 就是一个 DbForumFactory 的实例 , 由它来做具体的工作 . Authorization 可以认为是一个认证过的当前用户 ( 指实际的浏览器的使用者 ),ForumPermissions 可以认为是当前用户的权限 .

  public Forum createForum(String name, String description) 
            throws UnauthorizedException 
    { 
        // 这里就对权限进行了检查 , 具有系统管理员权限 , 则可以进行相应的操作 , 
        // 否则抛出异常 . 
        if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) { 
            Forum newForum = factory.createForum(name, description); 
            return new ForumProxy(newForum, authorization, permissions); 
        } 
        else { 
            throw new UnauthorizedException(); 
        } 
    } 
    public Forum getForum(int ID) throws ForumNotFoundException, 
            UnauthorizedException 
    { 
        Forum forum = factory.getForum(ID); 
        ForumPermissions forumPermissions = forum.getPermissions(authorization); 
        //Create a new permissions object with the combination of the 
        //permissions of this object and tempPermissions. 
        ForumPermissions newPermissions = 
                new ForumPermissions(permissions, forumPermissions); 
        //Check and see if the user has READ permissions. If not, throw an 
        //an UnauthorizedException. 
        if (!( 
            newPermissions.get(ForumPermissions.READ) || 
            newPermissions.get(ForumPermissions.FORUM_ADMIN) || 
            newPermissions.get(ForumPermissions.SYSTEM_ADMIN) 
            )) 
        { 
            throw new UnauthorizedException(); 
        } 
        // 同上所述 . 
        // 这里得到的 forum, 是一个 DbForum 的实例 , 跟 ForumFactory 一样 , 
        // 返回一个封装过的代理对象 , 来对 forum 进行权限控制 . 
        return new ForumProxy(forum, authorization, newPermissions); 
    }

其他所有的对象都是类似的 . 这里就不再赘述 .

行为型模式 (Behavioral Patterns)

这一类的模式关心的是算法以及对象之间的任务分配 . 它所描述的不仅仅是对象或类的设计模式 , 还有它们之间的通讯模式 .

1, 下来看看怎么从一个 Forum 中得到一些 Thread. 当然这里要涉及到数据库 , 我们先设计一个最简单的数据库表 , 表名 : thread, 字段 ThreadID int, ForumID int, 其他内容我们不关心 . 然后比如 Forum 中的一个方法 , getThreads() 来返回当前 Forum 所有的 Thread. 然后就可以这样做 :

         public ForumThread[] getThreads() 
           { 
                1, 从数据库里面查询 , 取出所有的 ThreadID, 
                2, 根据 ThreadID 构造 ForumThread 对象 , 
                3, 返回一个数组 . 
           }

这样做最省事 , 最简单了 , 但好不好呢 ? 还得看需求 , 比如我要求根据时间排序 , 就还得修改这个方法 , 也就是说需要修改 DbForum 对象 . 那为什么不把取 Thread 这个操作单独拿出来呢 ? 这样的好处就是功能独立化 , 使 DbForum 更简单 , 符合前面我们所提到的不要让对象负担太多的责任这个原则 . 也许你会说 , 如果要修改的话 , 不是都得修改吗 ? 放哪里是一样的 , 这样没错 , 但只限于很小的系统 , 如果系统一大 , 那么就可能做 DbForum 中的简单查询和一些比较复杂的查询的程序员就不是一个人 , 这样牵扯到需要改动的地方较多 , 但分离以后 , 只需要一个人改很少的地方就可以完成 . 回过头来再看看问题 , 这里要返回一群 ForumThread 对象 , 而且它们之间还可能有一定的先后关系 , 怎么来做这个工作呢 ? Iterator 设计模式是一个合适的选择 . Iterator 模式提供了一个连续访问一大群对象的方法 , 而不需要知道它们的表现形式 , 比如按什么方式排序等等 . 好了 , 来看看 Jive 的具体实现 . 由于 Java 本身已经有这样的接口 , Iterator 接口 , 所以只要实现这个接口就可以了。

From DbForum: 
    public Iterator threads() { 
        return new DbForumIterator(this, factory); 
    } 
 From DbForumIterator:  ( 做了改动 ) 
 public class DbForumIterator implements Iterator { 
     public DbForumIterator(...) 
     { 
        ... 
     } 
     public boolean hasNext()       // 是否还有元素
     { 
       ... 
     } 
     public Object next()      // 得到下一个元素
     { 
       ... 
     } 
     ... 
 }

那么 jsp 中可以这样访问 :

  Iterator threads = aForum.threads(); 
    while (threads.hasNext()) 
    { 
        ForumThread thread = (ForumThread)threads.next(); 
        做一些操作 . 
    }

从中可以看出 , 通过使用 Iterator 把 Threads 的一些具体细节进行了封装 , 提供统一的接口 . Jive 中这个设计模式也是用的非常多 , 多个用户显示 , 多个版面显示 , 多个线索 , 多个贴子都需要由它来实现 .

小结

上面我们一起探讨了一下设计模式在 Jive 中的应用情况 , 当然只是很简单 , 很肤浅 , 也很片面 , 不过总算能对设计模式有些认识 . 实际上 , 设计模式就是吸收许多前人的经验 , 把设计中一些重要的和重复出现的一些模式总结起来 , 给出一个系统的命名 , 给出相应的解释和评价 , 这个工作最先由 4 位软件大师所做 , 他们合写了一本书 --Design Pattern: Elements of Reusable Object-Oriented Software, 后来 , 人们把他们称为 GoF (Gang Of Four).

对于设计模式 , 可能在我们的实际项目中自觉不自觉地在使用着 , 比如 Factory Method 模式 , Abstract 模式 , Singleton 模式 , Iterator 模式 , 等等 , 只是概念不是非的明确 , 设计可能还有不太合理的地方 , 处于一种跟着感觉走的状态 , 相信很多有经验的设计者 , 原来没有接触设计模式 , 一旦接触以后 , 会有一种恍然大悟的想法 , 哈 , 原来是这么回事 . 学习设计模式 , 能很好地帮助我们设计 , 在相同的问题 , 相同的背景下 , 可以直接使用它 , 有的时候不知道该选择哪种好 , 就需对问题进行更深一层的分析 , 进行综合权衡 , 对设计模式也要进行更深刻的理解 , 才能得到好的结果 , 这也是一个进步的过程 .

对于笔者来说 , 刚刚接触设计模式 , 有了一点粗浅的理解 , 就冒昧写了这篇算是一点心得的东西 , 也是对自己的挑战 , 中间犯的一些错误 , 还请指正 , 谢谢 .


参考资料

  • Design Pattern: Elements of Reusable Object-Oriented Software,

  • Jive 源代码

关于作者

马旋 has authored this article


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值