JUnit源码分析(四)——从Decorator模式说起

简介:
其实我这系列小文,名为源码分析,其实是自己读《设计模式》的读书笔记。Decorator模式在java的IO库中得到应用,java的IO库看起来复杂,其实理解了Decorator模式再回头看可以很好理解并使用。
    Decorator模式,也就是装饰器模式,是对象结构型模式之一。

1.意图:动态地给一个对象添加一些额外的职责。给对象添加功能,我们首先想到的是继承,但是如果每增一个功能都需要继承,类的继承体系将无可避免地变的庞大和难以理解。面向对象设计的原则:优先使用组合,而非继承,继承的层次深度最好不过三。

2.适用场景:
1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加额外的责任
2)处理可以撤销的职责
3)为了避免类的数目爆炸,或者不能采用生成子类的方法进行扩展时

3.UML图和协作:

ShowImg.jpg

Component——定义一个对象接口,可以给这些对象动态地添加职责

ConcreteComponent——定义一个对象,可以给这个对象添加职责

Decorator——维持一个指向Component的引用,并定义一个与Component一致的接口,作为装饰类的父类

ConcreteDecorator——具体装饰类

4.效果:
1)与静态继承相比,Decorator可以动态添加职责,更为灵活
2)避免产生复杂的类,通过动态添加职责,而不是一次性提供一个万能的接口
3)缺点是将产生比较多的小对象,对学习上有难度,显然,java.io就是这个问题

我们以一个例子来实现Decorator模式,假设这样一个场景:在某个应用中需要打印票据,我们写了一个PrintTicket接口,然后提供一个实现类(DefaultPrintTicket)实现打印的功能:

package  com.rubyeye.design_pattern.decorator;
// 抽象component接口
public   interface  PrintTicket {
    
public   void  print();
}


// 默认实现类,打印票据
package  com.rubyeye.design_pattern.decorator;

public   class  DefaultPrintTicket  implements  PrintTicket {

    
public   void  print() {
        System.out.println(
" ticket body " );
    }

}

OK,我们的功能已经实现,我们还体现了针对接口编程的原则,替换一个新的打印方式很灵活,但是客户开始提需求了——人生无法避免的三件事:交税、死亡和需求变更。客户要求打印页眉,你首先想到的是继承:
package  com.rubyeye.design_pattern.decorator;

public   class  AnotherPrintTicket  implements  PrintTicket {

    
public   void  print() {
        System.out.println(
" ticket header " );
        System.out.println(
" ticket body " );
    }

}

请注意,我们这里只是简单的示例,在实际项目中也许意味着添加一大段代码,并且需要修改打印票据本体的功能。需求接踵而至,客户要求添加打印页码,要求增加打印花纹,要求可以联打......你的类越来越庞大,直到你看见这个类都想吐的地步!-_-。让我们看看另一个方案,使用Decorator模式来动态地给打印增加一些功能,首先是实现一个Decorator,它需要保持一个到PrintTicket接口的引用:

package  com.rubyeye.design_pattern.decorator;

public   class  PrintTicketDecorator  implements  PrintTicket {

    
protected  PrintTicket printTicket;

    
public  PrintTicketDecorator(PrintTicket printTicket) {
        
this .printTicket  =  printTicket;
    }

    
// 默认调用PrintTicket的print
     public   void  print() {
        printTicket.print();
    }

}

然后,我们实现两个具体的装饰类——打印页眉和页脚:
package  com.rubyeye.design_pattern.decorator;

public   class  HeaderPrintTicket  extends  PrintTicketDecorator {

    
public  HeaderPrintTicket(PrintTicket printTicket){
        
super (printTicket);
    }
    
    
public   void  print() {
        System.out.println(
" ticket header " );
        
super .print();
    }
}

package  com.rubyeye.design_pattern.decorator;

public   class  FooterPrintTicket  extends  PrintTicketDecorator {
    
public  FooterPrintTicket(PrintTicket printTicket) {
        
super (printTicket);
    }

    
public   void  print() {
        
super .print();
        System.out.println(
" ticket footer " );
    }
}

    使用起来也很容易:
   
package  com.rubyeye.design_pattern.decorator;

public   class  DecoratorTest {

    
/**
     * 
@param  args
     
*/
    
public   static   void  main(String[] args) {
        PrintTicket print
= new  HeaderPrintTicket( new  FooterPrintTicket( new  DefaultPrintTicket()));
        print.print();
    }

}

输出:
ticket header
ticket body
ticket footer

    了解了Decorator模式,我们联系了下JUnit里面的应用。作为一个测试框架,应该方便地支持二次开发,也许用户开发自己的TestCase,添加自定义的功能,比如执行重复测试、多线程测试等等。动态添加职责,而又不想使用静态继承,这正是Decorator使用的地方。在junit.extensions包中有一个TestDecorator,正是所有装饰类的父类,也是作为二次开发的基础,它实现了Test接口,而Test接口就是我们定义的抽象接口:
public   class  TestDecorator  implements   Test {
    
// 保有一个指向Test的引用
         protected  Test fTest;
       
    
public  TestDecorator(Test test) {
        fTest
=  test;
    }
        dot.gif
        
public   void  basicRun(TestResult result) {
          fTest.run(result);
        }
        
public   void  run(TestResult result) {
           basicRun(result);
        }
        dot.gif
}

Junit已经提供了两个装饰类:junit.extensions.ActiveTest用于处理多线程,junit.extensions.RepeatedTest用于执行重复测试,看看RepeatedTest是怎么实现的:
public   class  RepeatedTest  extends   TestDecorator {
        
// 重复次数
     private   int  fTimesRepeat;

    
public  RepeatedTest(Test test,  int  repeat) {
        
super (test);
        fTimesRepeat
=  repeat;
    }
        
public   void  run(TestResult result) {
        
// 重复执行
                 for  ( int  i =   0 ; i  <  fTimesRepeat; i ++ ) {
            
if  (result.shouldStop())
                
break ;
            
super .run(result);
        }
    }
        dot.gifdot.gif
}

    RepeatedTest继承 TestDecorator ,覆写 run(TestReult result)方法,重复执行,super.run(result)将调用传入的TestCase的run(TestResult result)方法,这已经在 TestDecorator默认实现。看看使用方式,使用装饰模式的好处不言而喻。

TestSuite suite  =   new  TestSuite();
suite.addTest(
new  TestSetup( new  RepeatedTest( new  Testmath( " testAdd " ), 12 )));
文章转自庄周梦蝶  ,原文发布时间5.17
目录
相关文章
|
30天前
|
IDE Java 测试技术
Junit 单元测试
JUnit是Java常用的单元测试框架,简化了测试用例的编写和执行。其特点包括简单注解、自动化测试、可扩展性、灵活性及与IDE的集成。使用方法涉及创建测试类、利用注解如@Test、@BeforeEach等管理测试生命周期,以及使用各种断言方法验证结果。此外,JUnit支持参数化测试以覆盖更多输入组合,并能与Maven、Gradle等构建工具集成,提升测试效率和项目管理。
36 1
|
3月前
|
Java 测试技术
JUnit 4 单元测试
单元测试是软件开发中必不可少的一环,但是在平常开发中往往因为项目周期紧,工作量大而被选择忽略,这样往往导致软件问题层出不穷。 线上出现的不少问题其实在有单元测试的情况下就可以及时发现和处理,因此培养自己在日常开发中写单元测试的能力是很有必要的。无论是对自己的编码能力的提高,还是项目质量的提升,都是大有好处,本文将介绍 Java 单元测试框架 JUnit 4 的基础认识和使用来编写单元测试,希望同样对你有所帮助。
|
6月前
|
Java 测试技术 API
Junit5单元测试框架详解
前面我们学习了Selenium自动化测试框架,但是有的时候测试用例会很多,我们需要一个工具来管理这些测试用例,而Junit就是一个很好的管理工具,简单点来说,Junit就是一个针对Java的单元测试框架;
99 0
|
6月前
|
Java 测试技术 程序员
|
7月前
|
测试技术
Junit单元测试
Junit单元测试
37 0
|
7月前
|
Java 程序员 网络安全
JUnit5学习之六:参数化测试(Parameterized Tests)基础
了解JUnit5的参数化测试的基本知识
115 2
JUnit5学习之六:参数化测试(Parameterized Tests)基础
|
4月前
|
测试技术
Junit单元测试 org.junit.jupiter.api.extension.ParameterResolutionException异常处理
Junit单元测试 org.junit.jupiter.api.extension.ParameterResolutionException异常处理
84 0