关于设计模式 , 这篇文章并不详细解释 , 只是结合 Jive 来看看设计模式在一个实际项目中的应用及其整体的设计思想 . 所以在读这篇文章前 , 假设您对设计模式有一个感性的认识 , 对其具体应用以及实现方法有些疑问 , 并渴望了解其思想 , 并使用过 Jive. 本文将一同来探讨这个问题 . 为什么选择 Jive 而不是选择一个新的例子重新开始呢 ? 有以下两个原因 : 1, 我们很多人对 bbs 这样一个事物比较熟悉 , 很清楚 bbs 所具有的一些基本功能 , 如果自己作为设计者来设计这样一个 web bbs, 会怎么想 , 再看看别人是怎么实现的 , 有对比才能明白自己设计上的缺点 , 看到别人的优点才能更快地进步 . 2, Jive 并不是非常地复杂 , 并且包括了一个完整的实现方案 , 从底层到高层 , 从后端到前端 , 都有很好的文档 , 这些都能更好地帮助我们理解它 .
这里我们所用的 Jive 的版本采用其开发者作为正式发布的 1.0 版 , 其最新版为 1.21, 对其结构作了少量改动 , 主要增加了 jsp tag 的支持 , 这种技术不属于我们的讨论范围 , 以后有机会可以共同学习 .
Jive 中所使用的设计模式 , 对设计模式的三种类型 -- 创建型 , 结构型 , 行为型 -- 都有涉及 , 这样也能比较全面地了解设计模式 . 我们先来自己设计一下 , 运用面向对象的思想 , 可以很容易知道 , 整个系统主要需要这几个对象 :
好了 , 我们需要的东西都在了 , 它们之间的关系十分复杂 , 怎么把它们组织地很符合我们的思路又能容易扩充呢 ? 我想大家都有自己的想法了 , "我能这么这么做","我可以这样这样设计", 我们一起来看看 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)
这一类型的设计模式 , 所要表现的是对象的创建过程及和用户所使用的对象之间的关系 .
|~~~~~~~~~~~~~~~~~|
| ForumFactory | abstract
|_________________|
| |
| extends |
| |
|~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~|
| ForumFactoryProxy | | DbForumFactory |
|____________________| |_________________|
( 图 3) |
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;
}
|
|~~~~~~~~~~~~~~~~~~~~|
| ForumMessage |
|____________________|
|
| implements
|
|~~~~~~~~~~~~~~~~| Prototype |~~~~~~~~~~~~~~~~~~~~~|
| ForumThread |-----------> | ForumMessageFilter |
|----------------| |---------------------|
| getMessage() o | | clone() |
|______________|_| |_____________________|
| / |
|~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~|
| aFilter.clone()| | HighlightCode | | HTML |
|________________| |---------------| |-------------| ......
| clone() o | | clone() o |
|___________|___| |___________|_|
| |
|~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~|
| 返回一个实例 | | 返回一个实例 |
|_______________| |_______________|
( 图 4) |
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 模式 , 等等 , 只是概念不是非的明确 , 设计可能还有不太合理的地方 , 处于一种跟着感觉走的状态 , 相信很多有经验的设计者 , 原来没有接触设计模式 , 一旦接触以后 , 会有一种恍然大悟的想法 , 哈 , 原来是这么回事 . 学习设计模式 , 能很好地帮助我们设计 , 在相同的问题 , 相同的背景下 , 可以直接使用它 , 有的时候不知道该选择哪种好 , 就需对问题进行更深一层的分析 , 进行综合权衡 , 对设计模式也要进行更深刻的理解 , 才能得到好的结果 , 这也是一个进步的过程 .
对于笔者来说 , 刚刚接触设计模式 , 有了一点粗浅的理解 , 就冒昧写了这篇算是一点心得的东西 , 也是对自己的挑战 , 中间犯的一些错误 , 还请指正 , 谢谢 .
参考资料
关于作者
马旋 has authored this article