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

    xiaoxiao2024-05-28  98

    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源码和文档

    文章转自庄周梦蝶  ,原文发布时间5.17

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)