天天看點

JUnit源碼分析(一)——Command模式和Composite模式

junit的源碼相比于spring和hibernate來說比較簡單,但麻雀雖小,五髒俱全,其中用到了比較多的設計模式。很多人已經在網上分享了他們對junit源碼解讀心得,我這篇小文談不出什麼新意,本來不打算寫,可最近工作上暫時無事可做,那就寫寫吧,結合《設計模式》來看看。

    我讀的是junit3.0的源碼,目前junit已經釋出到4.0版本了,盡管有比較大的改進,但基本的骨架不變,讀3.0是為了抓住重點,省去對旁支末節的關注。我們來看看junit的核心代碼,也就是junit.framework包,除了4個輔助類(assert,assertfailederror,protectable,testfailure),剩下的就是我們需要重點關注的了。我先展示一張uml類圖:

JUnit源碼分析(一)——Command模式和Composite模式

    我們先不去關注testdecorator類(此處是decorator模式,下篇文章再講),看看test接口,以及它的兩個實作類testcase和testsuite。很明顯,此處用到了command模式,為什麼要使用這個模式呢?讓我們先來看看什麼是command模式。

command模式

command模式是行為型模式之一

1.意圖:将一個請求封裝為一個對象,進而使你可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日志,以及支援可撤銷的操作。

2.适用場景:

1)抽象出待執行的動作以參數化對象,command模式是回調函數的面向對象版本。回調函數,我想大家都明白,函數在某處注冊,然後在稍後的某個時候被調用。

2)可以在不同的時刻指定、排列和執行請求。

3)支援修改日志,當系統崩潰時,這些修改可以被重做一遍。

4)通過command模式,你可以通過一個公共接口調用所有的事務,并且也易于添加新的事務。

3。uml圖:

JUnit源碼分析(一)——Command模式和Composite模式

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;

JUnit源碼分析(一)——Command模式和Composite模式
JUnit源碼分析(一)——Command模式和Composite模式

//具體指令二

public class testsuite implements test {

JUnit源碼分析(一)——Command模式和Composite模式

由此帶來的好處:

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圖:

JUnit源碼分析(一)——Command模式和Composite模式

圖中單個對象就是樹葉(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