788
技術社區[雲棲]
Digester:一個通用xml引擎的設計剖析
一: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上就有。
最後更新:2017-04-02 00:06:42