前言
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整体关系
你可能会有疑问,为什么会都是接口呢?这是基于扩展性考虑的。在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模式的实现类图
在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模式的类图
这个模式的实现基本上和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模式的类图
我们可以看到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模式的实现
在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设计中所有涉及到的设计模式的地方,基本上都讲完了,看完了之后,是不是对设计模式有了更进一步的了解了呢?
闽公网安备 35060202000074号