JUnit原始碼分析(一)——Command模式和Composite模式
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是具體的命令
由此帶來的好處:
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:
當執行run方法時遍歷這個集合,呼叫裡面每個TestCase物件的run()方法,從而執行測試。我們使用的時候僅僅需要把TestCase新增到集合內,然後用一致的方式(run方法)呼叫他們進行測試。
考慮使用Composite模式之後帶來的好處:
1)JUnit可以統一地處理組合結構TestSuite和單個物件TestCase,避免了條件判斷,並且可以遞迴產生更復雜的測試物件
2)很容易增加新的TestCase。
參考資料:《設計模式——可複用物件導向軟體的基礎》
《JUnit設計模式分析》 劉兵
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 Test 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 {
package junit.framework;
/**
* A Test 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);
}
}
/**
* 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原始碼和文件
相關文章
- 組合模式(Composite)模式
- composite pattern(組合模式)模式
- command模式模式
- [C++設計模式] composite 組合模式C++設計模式
- 設計模式之組合模式(Composite)分享設計模式
- 《設計模式》 - 7. 組合模式( Composite )設計模式
- 設計模式(十四)——模板模式(SpringIOC原始碼分析)設計模式Spring原始碼
- Seata原始碼分析(一). AT模式底層實現原始碼模式
- 設計模式-命令模式(Command)設計模式
- C#設計模式-組合模式(Composite Pattern)C#設計模式
- Retrofit原始碼分析二 代理模式原始碼模式
- 設計模式(九)——裝飾者模式(io原始碼分析)設計模式原始碼
- 設計模式(二十三)——策略模式(Arrays原始碼分析)設計模式原始碼
- Command 模式 Step by Step模式
- 設計模式(十八)——觀察者模式(JDK Observable原始碼分析)設計模式JDK原始碼
- 設計模式(十五)——命令模式(Spring框架的JdbcTemplate原始碼分析)設計模式Spring框架JDBC原始碼
- Android原始碼分析之備忘錄模式Android原始碼模式
- 設計模式(二十四)——職責鏈模式(SpringMVC原始碼分析)設計模式SpringMVC原始碼
- 設計模式(十七)——迭代器模式(ArrayList 集合應用原始碼分析)設計模式原始碼
- 設計模式(十二)——享元模式(Integer緩衝池原始碼分析)設計模式原始碼
- 【設計模式自習室】結構型:組合模式 Composite設計模式
- C++設計模式——命令模式(command pattern)C++設計模式
- fastadmin命令列模式--commandAST命令列模式
- Android原始碼設計模式-模板模式Android原始碼設計模式
- redis 原始碼分析:Jedis 哨兵模式連線原理Redis原始碼模式
- 設計模式:命令模式(Command Pattern)及例項設計模式
- JUnit4.8.2原始碼分析-4 RunNotifier與RunListener原始碼
- 命令模式 :Command(轉自LoveCherry)模式
- Android原始碼設計模式-中介者模式Android原始碼設計模式
- 【圖解設計模式系列】The Command Pattern: 命令列模式圖解設計模式命令列
- Seata 分散式事務框架 TCC 模式原始碼分析分散式框架模式原始碼
- Zookeeper原始碼分析(四) —– 叢集模式(replicated)執行原始碼模式
- React Fiber原始碼分析 第二篇(同步模式)React原始碼模式
- Zookeeper原始碼分析(三) ----- 單機模式(standalone)執行原始碼模式
- Zookeeper原始碼分析(四) ----- 叢集模式(replicated)執行原始碼模式
- Redis原始碼分析(三十)--- pubsub釋出訂閱模式Redis原始碼模式
- 基於 junit5 實現 junitperf 原始碼分析原始碼
- 設計模式(二十一)——直譯器模式(Spring 框架中SpelExpressionParser原始碼分析)設計模式Spring框架Express原始碼
- 原始碼中的設計模式--模板方法模式原始碼設計模式