• 适配器(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. JS 获取字符串实际长度

    解决思路,把中文转换为两个字节的英文,再计算长度. function getStrLength(str) { return str.replace(/[\u0391-\uFFE5]/g,"a ...

  2. 模拟退火算法实例(c++ 与 c# 实现)

    此片文章主要参考CSDN博主里头的一篇文章, 将自己的理解写下来,以方便后期的查阅. 一.C++ 实现 1. 已知平面上若干点坐标(xi, yi), 求平面上一点p(x, y) , 到这些点的总距离最 ...

  3. 阿里云正式上线移动直播问答解决方案,助力APP尽情“撒币”!

    2018年伊始,互联网圈就刮起了一阵"大佬狂撒币,网友喜答题"的热潮.以映客芝士超人等为代表的直播问答平台,通过答题分奖金的互动模式,迅速引爆网络热点.随后,多个直播和视频平台也上 ...

  4. 阿里云云虚拟主机安装Z-BlogPHP

    简介 在阿里云买了一个云虚拟主机,叫共享虚拟主机普惠版,6 块钱一年.本着练习上手的目的,尝试在阿里云云虚拟主机上安装Z-BlogPHP,一个个人建站的CMS 系统. 云虚拟主机网页空间为200M,M ...

  5. java_web学习(三) eclipse_jsp学习

    1.首先打开eclipse,新建一个Dynamac web project项目文件 2.在WebContent单击右键创建JSP File 3.过程 4.简单的jsp代码 运行结果: 5.导出war文 ...

  6. 统计0到n之间1的个数[数学,动态规划dp](经典,详解)

    问题描述 给定一个十进制整数N,求出从1到N的所有整数中出现”1”的个数.  例如:N=2时 1,2出现了1个 “1” . N=12时 1,2,3,4,5,6,7,8,9,10,11,12.出现了5个 ...

  7. bzoj:1675 [Usaco2005 Feb]Rigging the Bovine Election 竞选划区

    Description It's election time. The farm is partitioned into a 5x5 grid of cow locations, each of wh ...

  8. [51nod1310]Chandrima and XOR

    有这样一个小到大排列的无穷序列S:1, 2, 4, 5, 8......,其中任何一个数转为2进制不包括2个连续的1.给出一个长度为N的正整数数组A,A1, A2......An记录的是下标(下标从1 ...

  9. UVA 10382 - Watering Grass【贪心+区间覆盖问题+高精度】

    UVa 10382 - Watering Grass n sprinklers are installed in a horizontal strip of grass l meters long a ...

  10. BZOJ-USACO被虐记

    bzoj上的usaco题目还是很好的(我被虐的很惨. 有必要总结整理一下. 1592: [Usaco2008 Feb]Making the Grade 路面修整 一开始没有想到离散化.然后离散化之后就 ...