一:Digester介绍
Digester是Jakarta 子项目Commons下的一个模块,支持基于规则的对任意XML文档的处理。它最初是Structs项目的
一部分,后因其通用性而划归Commons.
本文不是描述Digester如何使用,而是深入分析Digester的源码,对其设计进行分析,从而从中学到设计方法和一些设计
理念,正所谓“授人鱼,不如授人以渔”。
二:不好的设计
Digester要解决的问题看起来很简单:根据xml文件定义,来生成指定的对象。估计大部分人的第一反应就是if-else,由于
xml类似是树形结构的,因此为了完成一个xml文件的解析,需要if-else加上递归才能完成一个xml文件的解析,例如下面的
xml文件,于下面的写法:
if( element = "book")
{
processBook();
}
if( element = "name ")
{
processName();
}
.....................................
从功能上来说,这样也确实是能够实现功能,但从设计角度来说,这样几乎是没有任何设计,为什么呢?
这样的设计存在的主要问题如下:
1)代码和xml结构和元素绑定,一旦xml结构变化,或者element的处理变化,解析代码就需要跟着调整;
2)由于上面问题,导致这个所谓的xml引擎无法被重用,每个不同的项目采用不同的xml文件,都需要重新编写代码。
总结来说,这个设计就是没有任何封装,无法重用,不能适应任何变化。
三:Digester设计分析
下面我们看看Digester是如何设计的。
通过上面的一个样例分析,我们知道设计一个通用xml引擎要面临的设计问题有:
(1)xml文档格式不定;(2)每个xml对象的处理方式是可以变化的。
那么,Digester是如何解决这两个问题呢?
我们首先来看Digester的运行机制,如下是Digester的类结构图:
1) 首先,Client需要创建一个Digester对象;
2) 然后,Client必须根据自己的xml格式来添加所有的Rule。
3) 添加完Rule后,Client调用Digester的parse操作来解析xml文件。
4) Digester实现了SAX的接口,解析时遇到具体的xml对象时会调用starElement等接口函数
5) 在这些SAX接口函数中,会扫描规则链rules(图中的RulesBase对象),找到匹配规则,规则匹配一般都是根据具体的元素名称来进行匹配。
6) 找到对应的rule后,依次执行rule。这里starElement对应的是begin操作,endElement对应的是end操作。具体要
做的事情都在每个rule的begin、body、end函数中。
7) 文档结束后,会执行所有rule的finish函数。
从以上的运行过程可以看出,Digester为了解决面临的两个问题,采用了巧妙的设计方法:
(1)xml格式变化:Digester把这个变化抛给具体用户去解决,用户在使用Digester之前必须自己根据自己的
xml文件格式来构造规则链,Digester只提供构造规则链的手段,体现了有所为有所不为的设计思想。
(2)“处理方式变化”的问题,Digester将“处理方式”抽象为“规则rule”,一个规则对应一个处理方式。
Digester提供了通用的缺省的Rule,如果用户觉得Digester提供的规则不满足自己的要求,可以自己另外定制,样例
dbinsert就体现了这种情况。
有了以上的设计,Digester完全就是一个通用的xml引擎了,只要你根据自己的应用写对应的Rule就OK了,
个人想到的应用有(以下仅为推测,没有真正考核过是否是这样实现):
1)根据xml文件创建对象:例如struts,Android中由xml定义view;
2)根据xml文件执行操作:例如Ant,数据库操作;
3)根据xml文件定义流程:例如数据流、业务流处理;
........................(其它请大家自己发散了:-P)
四:Digester设计总结
大部分人可能都知道设计模式中两个重要的设计原则“基于接口编程”和“封装变化”,但实际应用中如
体现呢?Digester为我们提供了很好的样例。
(1)基于接口编程:Digester的框架类都是接口或者虚类,例如Rules、Rule;
(2)封装变化:将具体的处理抽象为Rule,将xml的结构由用户去构造(因为这个无法抽象)。
顺便翻了一下《设计模式》,觉得Digester这种设计方法对应设计模式中的“策略模式”。
附:Digester源码CSDN上就有。