莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理
              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.意圖:將對象組合成樹形結構以表示“部分——整體”的層次結構。使得用戶對單個對象和組合結構的使用具有一致性。

          2.適用場景:
          1)想表示對象的部分-整體層次
          2)希望用戶能夠統一地使用組合結構和單個對象。具體到JUnit源碼,我們是希望用戶能夠統一地方式使用TestCase和TestSuite

          3.UML圖:

                

          圖中單個對象就是樹葉(Leaf),而組合結構就是Compoiste,它維護了一個Leaf的集合。而Component是一個抽象角色,給出了共有接口和默認行為,也就是JUnit源碼中的Test接口。

          4.效果:
          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源碼和文檔









              

          評論

          # re: JUnit源碼分析(一)——Command模式和Composite模式  回復  更多評論   

          2007-04-05 15:20 by 山風小子
          嗯,不錯,大有收獲,謝謝:)

          # re: JUnit源碼分析(一)——Command模式和Composite模式  回復  更多評論   

          2007-04-05 15:28 by leekiang
          你的uml圖用什么工具畫的?蠻漂亮的

          # re: JUnit源碼分析(一)——Command模式和Composite模式  回復  更多評論   

          2007-04-05 15:39 by dennis
          @leekiang

          用oracle的JDeveloper,從源代碼生成的UML圖的,呵呵
          主站蜘蛛池模板: 象山县| 辰溪县| 西青区| 广宁县| 长丰县| 石泉县| 和政县| 宁国市| 枝江市| 合水县| 蒙城县| 吴桥县| 长汀县| 郁南县| 雷山县| 城市| 黎城县| 宁都县| 苍山县| 南岸区| 孝义市| 特克斯县| 克拉玛依市| 周宁县| 霍林郭勒市| 大理市| 工布江达县| 南木林县| 湛江市| 景谷| 内江市| 高淳县| 施秉县| 招远市| 道真| 弥勒县| 大关县| 柘荣县| 遂溪县| 尼玛县| 花莲市|