天天看点

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