junit的源碼相比于spring和hibernate來說比較簡單,但麻雀雖小,五髒俱全,其中用到了比較多的設計模式。很多人已經在網上分享了他們對junit源碼解讀心得,我這篇小文談不出什麼新意,本來不打算寫,可最近工作上暫時無事可做,那就寫寫吧,結合《設計模式》來看看。
我讀的是junit3.0的源碼,目前junit已經釋出到4.0版本了,盡管有比較大的改進,但基本的骨架不變,讀3.0是為了抓住重點,省去對旁支末節的關注。我們來看看junit的核心代碼,也就是junit.framework包,除了4個輔助類(assert,assertfailederror,protectable,testfailure),剩下的就是我們需要重點關注的了。我先展示一張uml類圖:

我們先不去關注testdecorator類(此處是decorator模式,下篇文章再講),看看test接口,以及它的兩個實作類testcase和testsuite。很明顯,此處用到了command模式,為什麼要使用這個模式呢?讓我們先來看看什麼是command模式。
command模式
command模式是行為型模式之一
1.意圖:将一個請求封裝為一個對象,進而使你可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日志,以及支援可撤銷的操作。
2.适用場景:
1)抽象出待執行的動作以參數化對象,command模式是回調函數的面向對象版本。回調函數,我想大家都明白,函數在某處注冊,然後在稍後的某個時候被調用。
2)可以在不同的時刻指定、排列和執行請求。
3)支援修改日志,當系統崩潰時,這些修改可以被重做一遍。
4)通過command模式,你可以通過一個公共接口調用所有的事務,并且也易于添加新的事務。
3。uml圖:
4.效果:
1)指令模式将調用操作的對象與如何實作該操作的對象解耦。
2)将指令當成一個頭等對象,它們可以像一般對象那樣進行操縱和擴充
3)可以将多個指令複合成一個指令,與composite模式結合使用
4)增加新的指令很容易,隔離對現有類的影響
5)可以與備忘錄模式配合,實作撤銷功能。
在了解了command模式之後,那我們來看junit的源碼,test接口就是指令的抽象接口,而testcase和testsuite是具體的指令
//抽象指令接口
package junit.framework;
/**
* a <em>test</em> can be run and collect its results.
*
* @see testresult
*/
public interface test {
/**
* counts the number of test cases that will be run by this test.
*/
public abstract int counttestcases();
* runs a test and collects its result in a testresult instance.
public abstract void run(testresult result);
}
//具體指令一
public abstract class testcase extends assert implements test {
* the name of the test case
private final string fname;
//具體指令二
public class testsuite implements test {
由此帶來的好處:
1.客戶無需使用任何條件語句去判斷測試的類型,可以用統一的方式調用測試和測試套件,解除了客戶與具體測試子類的耦合
2.如果要增加新的testcase也很容易,實作test接口即可,不會影響到其他類。
3.很明顯,testsuite是通過組合多個testcase的複合指令,這裡使用到了composite模式(組合)
4.盡管未實作redo和undo操作,但将來也很容易加入并實作。
我們上面說到testsuite組合了多個testcase,應用到了composite模式,那什麼是composite模式呢?具體來了解下。
composite模式
composite模式是對象結構型模式之一。
1.意圖:将對象組合成樹形結構以表示“部分——整體”的層次結構。使得使用者對單個對象群組合結構的使用具有一緻性。
1)想表示對象的部分-整體層次
2)希望使用者能夠統一地使用組合結構和單個對象。具體到junit源碼,我們是希望使用者能夠統一地方式使用testcase和testsuite
3.uml圖:
圖中單個對象就是樹葉(leaf),而組合結構就是compoiste,它維護了一個leaf的集合。而component是一個抽象角色,給出了共有接口和預設行為,也就是junit源碼中的test接口。
1)定義了基本對象群組合對象的類層次結構,通過遞歸可以産生更複雜的組合對象
2)簡化了客戶代碼,客戶可以使用一緻的方式對待單個對象群組合結構
3)添加新的元件變的很容易。但這個會帶來一個問題,你無法限制元件中的元件,隻能靠運作時的檢查來施加必要的限制條件
具體到junit源碼,單個對象就是testcase,而複合結構就是testsuite,test是抽象角色隻有一個run方法。testsuite維護了一個testcase對象的集合ftests:
private vector ftests= new vector(10);
/**
* adds a test to the suite.
public void addtest(test test) {
ftests.addelement(test);
}
/**
* runs the tests and collects their result in a testresult.
public void run(testresult result) {
for (enumeration e= tests(); e.hasmoreelements(); ) {
if (result.shouldstop() )
break;
test test= (test)e.nextelement();
test.run(result);
}
當執行run方法時周遊這個集合,調用裡面每個testcase對象的run()方法,進而執行測試。我們使用的時候僅僅需要把testcase添加到集合内,然後用一緻的方式(run方法)調用他們進行測試。
考慮使用composite模式之後帶來的好處:
1)junit可以統一地處理組合結構testsuite和單個對象testcase,避免了條件判斷,并且可以遞歸産生更複雜的測試對象
2)很容易增加新的testcase。
參考資料:《設計模式——可複用面向對象軟體的基礎》
《junit設計模式分析》 劉兵
junit源碼和文檔
文章轉自莊周夢蝶 ,原文釋出時間5.17