• 适配器(Adapter)模式

  在软件系统中,由于环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?  这就要利用Adapter模式。





  • Adapter模式意图

  1,将一个类的接口转换成客户希望的另一个接口。

  2,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

  • 适配器(Adapter)模式的构成

  1,目标抽象角色(Target)

  定义客户要用的特定领域的接口。



  2,适配器(Adapter)

调用另一个接口,作为一个转换器。



  3,适配类(Adaptee)

  定义一个接口,Adapter需要接入。即系统中原有的类,需要适配使之可以利用。



  4,客户端(Client)

协同对象符合Adapter适配器。





  • 适配器的分类

  适配器模式(Adapter Pattern)主要分为三种:

  1.基于类的继承方式,也称类适配器。



  2.基于对象组合方式,也称对象适配器,推荐这种实现而不是上一种使用继承。



  3.缺省的适配器模式(AWT, Swing事件模型所采用的适配器模式)。



OK,现在我们分别来写几个例子:

1,基于类的继承方式。

package org.linkinpark.junit.testjunit;

/**
* @创建作者: LinkinPark
* @创建时间: 2016年2月5日
* @功能描述: 新需求的需要的接口
*/
public interface Target
{ // 新需求需要实现的接口
void method(); }
package org.linkinpark.junit.testjunit;

public class TargetImpl implements Target
{ @Override
public void method()
{
System.out.println("这里是新需求的方法。。。");
} }
package org.linkinpark.junit.testjunit;

/**
* @创建作者: LinkinPark
* @创建时间: 2016年2月5日
* @功能描述: 需要被适配的对象
*/
public class Adaptee
{ public void method()
{
System.out.println("这里是原来的方法。。。");
} }
package org.linkinpark.junit.testjunit;

/**
* @创建作者: LinkinPark
* @创建时间: 2016年2月5日
* @功能描述: 适配器
*/
public class Adapter extends Adaptee implements Target
{ @Override
public void method()
{
super.method();
} }
package org.linkinpark.junit.testjunit;

import org.junit.Test;

public class AdapterTest
{ @Test
public void testAdapter()
{
Target target = new Adapter();
target.method(); Target target1 = new TargetImpl();
target1.method();
} }

OK,现在我们来看下控制台的输出,OK,完美的兼容了新旧版本。

这里是原来的方法。。。
这里是新需求的方法。。。



2,基于组合的方式

其他类不用动,只是将原来的适配器由继承旧对象变成组合旧对象就OK了。代码如下:

package org.linkinpark.junit.testjunit;

/**
* @创建作者: LinkinPark
* @创建时间: 2016年2月5日
* @功能描述: 适配器
*/
public class Adapter implements Target
{
private Adaptee adaptee; // 这里可以构造器注入旧对象,也可以在上面的属性set()设值
public Adapter(Adaptee adaptee)
{
super();
this.adaptee = adaptee;
} @Override
public void method()
{
adaptee.method();
} }
package org.linkinpark.junit.testjunit;

import org.junit.Test;

public class AdapterTest
{ @Test
public void testAdapter()
{
Target target = new Adapter(new Adaptee());
target.method(); Target target1 = new TargetImpl();
target1.method();
} }

运行上面的测试代码,控制台同样的输出,OK,没问题。

3,关于第三种方式,这里不做赘述了。就是在有多个方法的接口和一个不想实现那么多方法的实现类中加入实现所有接口方法的一层,加入的这一层的实现可以是空实现,这个无所谓。在自己真正需要的实现类中去重写加入的那一层里面自己敢兴趣的方法就OK了。



来总结一下:

  • 适配器(Adapter)模式的适用性

  1,对象需要利用现存的并且接口不兼容的类。



  2,需要创建可重用的类以协调其他接口可能不兼容的类。

  • JUnit中的适配器模式:

  1,JUnit3.8的TestCase的源代码中,真正运行测试源码的代码如下,

public void runBare() throws Throwable {
setUp();
try {
runTest();
}
finally {
tearDown();
}
}

runTest()中执行我们的测试方法。测试方法testXXX要求必须是public void并且不带参数的,这里就使用了适配器模式。runTest()的实现如下:

protected void runTest() throws Throwable
{
assertNotNull("测试的方法名不能为空", fName);
Method runMethod = null;
try
{
runMethod = getClass().getMethod(fName, (Class[]) null);
}
catch (NoSuchMethodException e)
{
fail("Method \"" + fName + "\" not found");
}
if (!Modifier.isPublic(runMethod.getModifiers()))
{
fail("Method \"" + fName + "\" should be public");
} try
{
System.out.println(String.format("框架开始执行测试,执行的方法是-->%s", runMethod));
runMethod.invoke(this);
System.out.println(String.format("框架结束执行测试,执行的方法是-->%s", runMethod));
}
catch (InvocationTargetException e)
{
e.fillInStackTrace();
throw e.getTargetException();
}
catch (IllegalAccessException e)
{
e.fillInStackTrace();
throw e;
}
}

2,在junit4X系列中,junit也给我们向后兼容了38系列,这里用到的也是适配器模式。OK,我们来看一些核心代码:

Junit4X系列测试入口是junitCore类:

public class JUnitCore
{
private final RunNotifier notifier = new RunNotifier(); /**
* @创建时间: 2016年1月22日
* @相关参数: @param args
* @功能描述: 框架测试入口
*/
public static void main(String... args)
{
String[] linkinArgs = new String[] { "org.linkinpark.commons.textui.LinkinTest4Annotation" };
Result result = new JUnitCore().runMain(new RealSystem(), linkinArgs);
System.exit(result.wasSuccessful() ? 0 : 1);
}
}

/**
* @创建时间: 2016年1月22日
* @相关参数: @param system
* @相关参数: @param args
* @相关参数: @return 测试结果
* @功能描述: 框架核心代码开始执行入口
*/
Result runMain(JUnitSystem system, String... args)
{
system.out().println("linkin-frame-junit测试框架版本号==> " + Version.id()); System.out.println("第一步:框架开始执行====");
JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult.parse(args);
System.out.println("第二步:开始添加事件====");
RunListener listener = new TextListener(system);
addListener(listener);
System.out.println("第三步:开始运行测试====");
return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
}

关键是最后一行代码,jUnitCommandLineParseResult.createRequest()创建一个Request对象返回给测试执行器执行。OK,我们接着看下Request对象的这个方法:

public static Request classes(Computer computer, Class<?>... classes)
{
try
{
// 默认的可以执行所有的测试执行器builder
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
Runner suite = computer.getSuite(builder, classes);
return runner(suite);
}
catch (InitializationError e)
{
return runner(new ErrorReportingRunner(e, classes));
}
}

AllDefaultPossibilitiesBuilder是所有可能执行的测试执行器的builder,在Request中junit4X用默认的策略类computer来初始化测试执行器。

public Runner getSuite(final RunnerBuilder builder, Class<?>[] classes) throws InitializationError
{
return new Suite(new RunnerBuilder()
{
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable
{
return getRunner(builder, testClass);
}
}, classes);
} protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable
{
return builder.runnerForClass(testClass);
}

核心代码来了:

@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable
{
List<RunnerBuilder> builders = Arrays.asList(ignoredBuilder(), annotatedBuilder(), suiteMethodBuilder(), junit3Builder(), junit4Builder());
for (RunnerBuilder each : builders)
{
Runner runner = each.safeRunnerForClass(testClass);
if (runner != null)
{
return runner;
}
}
return null;
} protected JUnit4Builder junit4Builder()
{
return new JUnit4Builder();
} protected JUnit3Builder junit3Builder()
{
return new JUnit3Builder();
} protected AnnotatedBuilder annotatedBuilder()
{
return new AnnotatedBuilder(this);
} protected IgnoredBuilder ignoredBuilder()
{
return new IgnoredBuilder();
} protected RunnerBuilder suiteMethodBuilder()
{
if (canUseSuiteMethod)
{
return new SuiteMethodBuilder();
}
return new NullBuilder();
}

循环迭代所有的可能的测试执行器,如果是38系列的测试源码,也就是说我们自己写的测试类继承于TestCase,那么就会给我们返回一个38系列的测试执行器-->Junit3Builder。

package org.linkinpark.junit.internal.builders;

import org.linkinpark.commons.framework.TestCase;
import org.linkinpark.junit.runner.Runner;
import org.linkinpark.junit.runners.JUnit38ClassRunner;
import org.linkinpark.junit.runners.model.RunnerBuilder; public class JUnit3Builder extends RunnerBuilder
{
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable
{
if (isPre4Test(testClass))
{
return new JUnit38ClassRunner(testClass);
}
return null;
} boolean isPre4Test(Class<?> testClass)
{
return TestCase.class.isAssignableFrom(testClass);
}
}

OK,接下来就可以嫁入junit38框架来运行测试了呢。

public class JUnit38ClassRunner extends Runner implements Filterable, Sortable
{
private volatile Test test; @Override
public void run(RunNotifier notifier)
{
TestResult result = new TestResult();
result.addListener(createAdaptingListener(notifier));
getTest().run(result);
}
}

OK,关于junit4我后面会专门整理到,这里简单说下这个Runner,该类是junit4X系列中所有运行执行器父类,该类里面封装一个run()的抽象方法来运行测试用例。

其实原理和38系列的差不多,但是关于注解的处理那一刻值得我们去学习下的。

这里以runner抽象类来结束这篇博客:

package org.linkinpark.junit.runner;

import org.linkinpark.junit.runner.notification.RunNotifier;

/**
* @创建作者: LinkinPark
* @创建时间: 2016年1月23日
* @功能描述: 所有测试执行器的父类
*/
public abstract class Runner implements Describable
{
public abstract Description getDescription(); public abstract void run(RunNotifier notifier); public int testCount()
{
return getDescription().testCount();
}
}

junit设计模式--适配器模式的更多相关文章

  1. 20. 星际争霸之php设计模式--适配器模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  2. Java设计模式——适配器模式

    JAVA 设计模式 适配器模式 用途 适配器模式 (Adapter) 将一个类的接口转换成客户希望的另外一个接口. Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适配器 ...

  3. linkin大话设计模式--适配器模式

    linkin大话设计模式--适配器模式 大家知道,在java中只允许单继承,但是在实际问题中往往都需要多继承,java引入了接口这一概念.(一个类可以实现多个接口) 由于接口中都是抽象方法,那么我们在 ...

  4. php设计模式适配器模式

    php设计模式适配器模式 简介 适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的.一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起. 其实就是通过一个转换类,这个转 ...

  5. 【设计模式】Java设计模式 - 适配器模式

    [设计模式]Java设计模式 - 适配器模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一 ...

  6. [Head First设计模式]身边的设计模式——适配器模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  7. JAVA 设计模式 适配器模式

    用途 适配器模式 (Adapter) 将一个类的接口转换成客户希望的另外一个接口. Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适配器模式是一种结构型模式. 结构

  8. Java设计模式 - 适配器模式

    概念: 将一个类的接口,转换成客户期望的另一个接口.适配器模式让原来接口不兼容的类可以在一起工作. 解决的问题: 提供类似于中间人的作用:把原本不兼容.不能一起工作的接口组合在一起,使得它们能够在一起 ...

  9. 24种设计模式--适配器模式【Adapter Pattern】

    今天讲适配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,虽然两个国家的的电源电压不同,中国是 220V,日本是 110V,但是这个适配器能够把 ...

随机推荐

  1. [转]OpenLiveWriter 代码插件

    插件地址链接:http://pan.baidu.com/s/1jHFDtbS 密码:ax31 将文件解压,放在路径下面 重启应用后,如图

  2. while100以内的偶数

    #显示100以内的偶数 #声明i i = 1 #开始循环条件为i不等于100,执行while代码块 while i != 100: #给i加1 i +=1 #如果循环到此时i的取余运算为0则打印i i ...

  3. ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)

    前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期. 这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 目录 ...

  4. 原生js选项卡

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. SPI通讯协议

    一.SPI概述 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线, ...

  6. 使用Anaconda搭建TensorFlow-GPU环境

    前言: 对于深度学习来说,各种框架torch,caffe,keras,mxnet,tensorflow,pandapanda环境要求各一,如果我们在一台服务器上部署了较多的这样的框架,那么各种莫名的冲 ...

  7. CSRF跨站

    跨站请求伪造: 简单的说跨站请求伪造就是一些恶意的用户用自己的表单伪造网页实际的表单发送数据,接下来我就随便写一点: 跨站伪造的产生(form表单的methoud只有在等于post的时候才会有可能发生 ...

  8. 大数据学习系列之九---- Hive整合Spark和HBase以及相关测试

    前言 在之前的大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 中介绍了集群的环境搭建,但是在使用hive进行数据查询的时候会非常的慢,因为h ...

  9. 从“思考”的角度来看如何成为一名优质的Java架构师

    导读: 架构师应不应该写代码 为什么别人的系统总是那么烂 成为架构师最困难的门槛是什么? 如何更高效的学习? 1.架构师应不应该写代码 合格的程序员对于明确分配的任务会完成的很好,但是大部分情况下&q ...

  10. Gym 100952C&&2015 HIAST Collegiate Programming Contest C. Palindrome Again !!【字符串,模拟】

    C. Palindrome Again !! time limit per test:1 second memory limit per test:64 megabytes input:standar ...