问题
我们已经应用了Command模式来表现一个测试用例。Command依赖于一个单独的像execute()这样的方法(在TestCase中称为run())来对其进行调用。这样允许我们能够通过相同的接口来调用一个command的不同实现。
如果实现一个测试用例,就必须实现继承Testcase,然后实现run方法,实际是testRun,测试人员就把所有的测试用例都继承相同的类,这样的结果就会造成产生出大量的子类,使系统的测试维护相当困难,并且setUp和tearDown仅为这个testRun服务,其他的测试也必须完成相应的代码,从而增加了开发人员的工作量,怎样解决这个问题?
为了避免类的急剧扩散,试想一个给定的测试用例类可以实现许多不同的方法,每一个方法都有一个描述性的名称,如testGetMessage或testSetMessage。这样测试用例并不符合简单的command接口。因此又会带来另外一个问题就是,使所有测试方法从测试调用者的角度(JUnit框架)上看都是相同的。怎样解决这个接口不匹配问题?
思考设计模式的适用性,Adapter(适配器)模式便映入脑海。Adapter具有以下意图“将一个类的接口转换成客户希望的另外一个接口”。这听起来非常适合。把具有一定规则的描述性方法如testGetMessage,转化为JUnit框架所期望的Command(TestCase的run)从而方便框架执行测试。Adapter模式又分为类适配器和对象适配器。类适配器是静态的实现在这里不适合使用,于是使用了对象适配器。
适配器模式的构成:
1、Target 系统所期望的目标接口
2、 Adaptee 现有需要适配的接口
3、 Adapter 适配器角色,把源接口转化成目标接口
模式的记忆方式:
假如我们家的台灯的插头是2相的,但是我们墙壁上的电源插座是3相的,那我们如何用呢???可以去买一个插排,插排接电源的是3孔的,插排上有很多插孔是2孔的,那么插排起到的作用就是桥梁,起转换的作用,在这里就是适配器模式中的适配器。
适配器模式的代码实现:
Target: //台灯插头
public interface Target {
public void method1();
}
Adapter: //插排
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void method1() {
adaptee.method2();
}
}
Adaptee: //墙壁上的电源
public class Adaptee {
public void method2() {
System.out.println("method2");
}
}
Client:
public class Client {
public static void main(String[] args) {
Target target = new Adapter(new Adaptee());
target.method1();
}
}
在实现对象的适配时,首先在TestCase中定义测试方法的命名规则必须是public void testXXXXX()这样我们解析方法的名称,如果符合规则认为是测试方法,然后使用Adapter模式把这些方法,适配成Command的runTest方法。在实现时使用了java的反射技术,这样便可很容易实现动态适配。代码如下
protected void runTest() throws Throwable {
//使用名称获得对象的方法,如testGetMessage,然后动态调用,适配成runTest方法
Method runMethod= getClass().getMethod(fName, null);
runMethod.invoke(this, new Class[0]);
}
在这里目标接口Target和适配器Adapter变成了同一个类,TestCase,而测试用例,作为Adaptee
效果
我们来考虑经过使用Adapter模式后给系统的架构带来了那些效果:
1、使用Adapter模式简化测试用例的开发,通过按照方法命名的规范来开发测试用例,不需要进行大量的类继承,提高代码的复用,减轻测试人员的工作量
2、使用Adapter可以重新定义Adaptee的部分行为,如增强异常处理等