Jive 中的设计模式

来源:互联网 发布:c语言编程代码 编辑:程序博客网 时间:2024/05/17 23:44

关于设计模式 , 这篇文章并不详细解释 , 只是结合 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


原创粉丝点击