适配器模式(Adapter)
将一个新的接口转换为已经存在的接口类型,从而方便被调用。
在上一节我们分析命令模式的时候,每个测试用例都会被封装成一个TestCase对象,请求Junit框架来执行,执行的是默认的run方法,当我们有多个测试用例时,就要封装这样的多个Command对象,工作量很大,如果我们定义一个类,其中的每个方法就代表一个测试用例,在被Junit框架调用时,在将每个方法转换为一个Command对象,即一个TestCase,这样Junit框架不需要做任何改变,仍然执行TestCase的run方法即可。
适配器模式有三个角色,如下:
Target 系统所期望的目标接口
Adaptee 现在需要适配的接口,源接口
Adapter 适配器,将源接口转为目标接口
下面看源码分析Junit框架中,适配器模式中各个角色是如何实现的:
Test、TestCase接口的角色就是Target
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); }
用户编写的测试用例类角色就是Adaptee,需要将其中每个方法转为测试框架所期望的Test接口。
适配器Adapter角色就由TestSuit来充当,首先看到的是构造方法:
public TestSuite(final Class theClass) { fName= theClass.getName(); try { getTestConstructor(theClass); // Avoid generating multiple error messages } catch (NoSuchMethodException e) { addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()")); return; }if (!Modifier.isPublic(theClass.getModifiers())) { addTest(warning("Class "+theClass.getName()+" is not public")); return; } Class superClass= theClass; Vector names= new Vector(); while (Test.class.isAssignableFrom(superClass)) { Method[] methods= superClass.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { addTestMethod(methods[i], names, theClass); } superClass= superClass.getSuperclass(); } if (fTests.size() == 0) addTest(warning("No tests found in "+theClass.getName()));
}
该构造方法参数为用户编写的测试用例类的class类型,然后获取该类对象中所有的方法对象,再由addTestMethod 方法将所有的以test开头的方法生成一个TestCase类或者其子类;
private void addTestMethod(Method m, Vector names, Class theClass) { String name= m.getName(); if (names.contains(name)) return; if (! isPublicTestMethod(m)) { if (isTestMethod(m)) addTest(warning("Test method isn't public: "+m.getName())); return; } names.addElement(name); addTest(createTest(theClass, name)); }
在看看其中核心方法createTest是如何构造一个TestCase类或者其子类;
static public Test createTest(Class theClass, String name) { Constructor constructor; try { constructor= getTestConstructor(theClass); } catch (NoSuchMethodException e) { return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"); } Object test; try { if (constructor.getParameterTypes().length == 0) { test= constructor.newInstance(new Object[0]); if (test instanceof TestCase) ((TestCase) test).setName(name); } else { test= constructor.newInstance(new Object[]{name}); } } catch (InstantiationException e) { return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")")); } catch (InvocationTargetException e) { return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")")); } catch (IllegalAccessException e) { return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")")); } return (Test) test; }
这样生成的每个TestCase类(或者其子类)就是由适配器转换后的目标接口类型了。当然,在用户编写测试用例类时,约定每个测试方法必须以test开头,适配器才能适配成功;
转换后的TestCase的会被放在一个Vector中,再组装成一个TestSuit对象,然后再由测试框架来调用,TestCase为什么会组装成TestSuit对象?这个就是下一节将要分析的组合模式,将会更加精彩!
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于