设计模式系列之适配器模式(Adapter Pattern)——不兼容结构的协调 (2)

双向适配器: 在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。

双向适配器结构图

典型代码实现如下:

class Adapter implements Target, Adaptee { //同时维持对抽象目标类和适配者的引用 private Target target; private Adaptee adaptee; public Adapter(Target target) { this.target = target; } public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); } @Override public void specificRequest() { target.request(); } }

在实际开发中,我们很少使用双向适配器。违背了单一职责原则,相当于一个适配器承担了两个适配器的职责。

缺省适配器

缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。

缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。

缺省适配器结构图

典型代码实现如下:

abstract class Adapter implements Target { @Override public void request1() { // 空实现,让具体实现类去有选择地实现 } @Override public void request2() { // 空实现,让具体实现类去有选择地实现 } @Override public void request3() { // 空实现,让具体实现类去有选择地实现 } } class ConcreteAdapter extends Adapter { // 维持一个对适配者对象的引用 private Adaptee adaptee; // 构造注入适配者 public Adapter(Adaptee adaptee) { this.adaptee=adaptee; } @Override public void request1() { // 只实现request1 adaptee.specificRequest(); } } 模式应用 模式在JDK中的应用

在JDK中,IO类中也大量使用到了适配器模式。比如说StringReader将String适配到Reader,InputStreamReader将InputStream适配到Reader等等。

这里用StringReader来说明。这里的StringReader相当于上述的Adapter,Reader相当于上述的Target,String相当于上述的Adaptee

public class StringReader extends Reader { // 维持对adaptee对象的引用 private String str; private int length; private int next = 0; private int mark = 0; /** * 构造注入一个String用于之后的read操作 */ public StringReader(String s) { this.str = s; this.length = s.length(); } // 这里相当于是在做适配操作,转为目标对象所期望的请求 public int read() throws IOException { synchronized (lock) { ensureOpen(); if (next >= length) return -1; return str.charAt(next++); } } } 模式在开源项目中的应用

其实不只是开源项目,我们自己写的项目很多地方都是隐含着适配器模式,只是有时候这种特性表现的不是很明显(因为我们很自然去使用),以至于我们都没有给类名命成XxxAdapter,比如说我们使用第三方库,第三方库某方法名太长或者参数过多,或者调用过于复杂了,我们可能会对第三方库再次做个封装,把适合自己项目当前业务逻辑的默认参数,默认实现补充完整,让其他地方很方便调用,举个具体例子,项目中可能经常会用到HttpClient,大多数情况下,对现有的HttpClient再次封装(比如client的创建、http响应结果的统一处理等等),封装成方便自己项目使用的SpecialHttpClient,如果你还想切换不同的底层HttpClient实现,还可以对SpecialHttpClient抽出来一个接口,通过不同的Adapter来注入不同的HttpClient(比如apache的HttpClient、OkHttpClient、Spring的RestTemplate以及WebClient等等)来实现,这种很自然的思想 个人觉得本质上也用到了适配器模式,相当于是把第三方的HttpClient适配成了自己的SpecialHttpClient。

当转换的源不是单一的时候,这种适配器思想就凸显出来了(对应上面的例子就是说 项目中需要同时用到apache的HttpClient、Spring的RestTemplate以及WebClient等)。

这里举个Spring中的例子。在Spring的AOP中,由于Advisor需要的是MethodInterceptor对象,所以每一个Advisor中的Advice都要适配成对应的MethodInterceptor对象

public interface AdvisorAdapter { boolean supportsAdvice(Advice advice); MethodInterceptor getInterceptor(Advisor advisor); } class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable{ @Override public boolean supportsAdvice(Advice advice) { return (advice instanceof ThrowsAdvice); } @Override public MethodInterceptor getInterceptor(Advisor advisor) { return new ThrowsAdviceInterceptor(advisor.getAdvice()); } } 模式总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsffgs.html