设计模式(十三):适配器模式(Adapter)

star2017 1年前 ⋅ 675 阅读

在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配(转换)。例如,使用电脑电源适配器将220V家用电转换为电脑需要的电。

在软件设计中,也可能出现需要的功能在现有的组件库中已存在,但与现有系统并不兼容,这时使用适配器模式就可以将并不兼容的接口转换为客户希望的目标接口。

使用适配器模式来处理像货物的包装过程,被包装的货物的真实样子被包装所掩盖和改变,因此也被叫做包装(Wrapper)模式。

模式定义

适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,使原本因不兼容而无法一起工作的那些接口能在一起工作。其别名为包装器(Wrapper)。

适配器模式目的是解决接口不兼容问题,通过增加中间适配器使源和目标能对接。例如,出国旅游需要准备插座转换头以给手机充电器用,这个转换头就相当于适配器,不同国家的插座标准不一样,需要转换头将国内的手机充电头与旅游国插座进行适配。本人去过印尼巴厘岛,其电源插座是德标,双圆孔插座,要给手机充电需要带德标的转换插头。

适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

  • 类结构型模式

    类的适配器模式把被适配的类(源类)的 API 转换成为目标类的 API。适配器类把 源类(Adaptee)的 API 与 目标类(Target)的 API 衔接起来。

    适配器类与源类(Adaptee)是继承关系,这决定了这个适配器模式是类的。

  • 对象结构型模式

    对象的适配器模式把被适配的类的 API 转换成为目标类的 API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到 Adaptee 类,而是使用委派关系连接到 Adaptee 类。

    提供一个包装(Wrapper)类 Adapter,这个包装类包装了一个 Adaptee 的实例,从而此包装类能够把 Adaptee 的 API 与 Target 类的 API 衔接起来。Adapter 与 Adaptee 是委派关系,这决定了此适配器模式是对象的。

适配器模式的用意是将接口不同而功能相同或相近的两个接口加以转换,这里面包括适配器角色补充了源角色没有的方法,但是目标接口需要的方法。

模式分析

模式结构

适配器模式(Adapter)所涉及的主要角色:

  • (Adaptee)角色:现有需要适配的接口。
  • 目标(target)角色:就是所期待得到的接口,目标可以是具体的或抽象的类。
  • 适配器(Adapter)角色:适配器类,继承或引用 源角色,重定义接口适配到目标接口。
    适配器类是该模式的核心,不可以是接口,必须是具体类。

优缺点

优点

  • 可以把多种不同的源适配到同一个目标一起工作,将目标类与源类解耦,解决目标类和源类接口不一致的问题。
  • 增加了类的透明性和复用性,将具体的实现封装在源类中,对客户端来说是透明的,而且提高了适配器类的复用性。
  • 灵活性好。要覆盖源类的方法并不容易,但要增加一些新的方法则很方便,且适用于所有的源。类适配器模式中的适配器是源的子类,可以在适配器中覆盖掉源的一些方法。

缺点

  • 过多地使用适配器,会让系统非常零乱,不易整体把握。
  • 由于 JAVA 至多继承一个类,所以至多只能适配一个源类,而且目标类必须是抽象类。

结构图与代码

类适配器模式

类适配器模式:适配器继承 角色,实现目标接口,在实现的目标接口中调用源角色的接口。

适配器继承源角色

源角色

public abstract class Adaptee {
    void specificRequest() {    }
}

目标角色

public interface Target {
    void request();
}

适配器角色

public class Adapter extends Adaptee implements Target  {
    @Override
    public void request() {
        super.specificRequest();
    }
}

客户端调用

public class Client {

    public static void main(String[] args) {
        Target target = new Adapter();
        target.request();
    }
}

对象适配器模式

类适配器模式:适配器引用 角色,实现目标接口,在实现的目标接口中委托 源 对象调用接口。

适配器继承源角色

源角色

public class Adaptee {
    public void specificRequest(){
        System.out.println("specificRequest");
    }
}

目标角色

public interface Target {
    void request();
}

适配器角色

public class Adapter implements Target {

    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

客户端调用

public class Client {

    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

应用场景

  • 系统需要使用现有的类,而此类的接口不符合系统的需求。
  • 构建一个可以重复使用的类,用于将一些没有太大关联的类衔接起来一起工作,这些源类不一定是很复杂的接口。
  • 当一个客户端只知道一个特定的接口,但又必须与具有不同接口的类打交道时,就应当使用适配器模式。

缺省适配器

即一个抽象类实现了目标接口,抽象类中对某些方法提供默认实现,适配器类继承抽象类,只需重写部分方法,而不是目标接口的所有方法,即适配器类不必实现不需要的那些接口。。

注意事项

  • 目标接口可以省略。此时,目标接口和源接口就是相同的。因源是一个接口,而适配器是一个类(或抽象类),就不必要实现不需要的方法。
  • 适配器类可以是抽象类。
  • 带参的的适配器模式。使用此方式,适配器可以根据参数返还一个合适的实例。

典型应用

JDBC驱动与适配器模式

JDK 提供的数据库连接工具 JDBC是一套通用接口标准,使用 Java 语言程序可以连接到数据库上。每一个数据库引擎的 JDBC 驱动软件都是一个介于 JDBC 接口和数据库引擎接口之间的适配器软件。

  • :各个数据库引擎提供的 API。

  • 目标:JDK 提供的抽象的 JDBC 接口。例如:Connection 接口,DataSource 接口。

  • 适配器:各个数据库提供的 JDBC 驱动软件(库)。例如:MySQL 的 mysql-connector-java, 提供的 ConnectionWrapper 对连接进行了包装,MysqlDataSource 实现的是 DataSource 接口。

    所以开发在实际使用的时候,很少关注具体的数据库驱动软件的接口,全是基于 JDK 的 JDBC 操作,也就是各个数据库引擎的 API 接口差异被屏蔽了,是通过驱动软件采用的适配器模式适配到目标 JDK 的 JDBC 接口。

JDK中的InputStreamAdapter

JDK 库中的也有一些适配器类,如 InputStreamAdapter,继承了 InputStream 接口,包装了 ImageInputStream 接口及其子类对象。

InputStreamAdapter 中采用的是类适配器模式结构,源码如下:

  • :ImageInputStream
  • 目标:InputStream
  • 适配器:InputStreamAdapter
public class InputStreamAdapter extends InputStream {
    ImageInputStream stream;

    public InputStreamAdapter(ImageInputStream stream) {
        super();

        this.stream = stream;
    }

    public int read() throws IOException {
        return stream.read();
    }

    public int read(byte b[], int off, int len) throws IOException {
        return stream.read(b, off, len);
    }
}

Spring MVC 中的适配器模式

Spring MVC 中的处理器适配器也是适配器模式的典型应用。

DispatcherServlet 做请求分发处理时,要找到对应的处理控制器(Controller)及方法。Spring MVC 中有多种类型的 Controller,不同类型的 Controller 都有自己的内部方法来处理请求(不同的方法来对请求进行处理)。

@FunctionalInterface
public interface Controller {

    /**
     * 处理请求并返回DispatcherServlet将呈现的ModelAndView对象
     */
    @Nullable
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

如果不使用适配器的话,就需要在 DispatcherServlet 中判断每个类型的 Controller 是否支持当前请求的 映射处理器。类似如下:

if(mappedHandler.getHandler() instanceof MultiActionController){  
   ((MultiActionController)mappedHandler.getHandler()).handleRequest(request, response);
}else if(mappedHandler.getHandler() instanceof XxxController){  
    ...  
}else if(...){  
   ...  
}

使用适配器后,上面的工作就交由适配器完成了,DispatcherServlet 只需要拿到适配器调用统一的接口方法(ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler))就行了。

适配器结构分析:

  • :不同类型的 Controller 的请求处理方法
  • 目标:HandlerAdapter,处理器适配器抽象
  • 适配器:HandlerAdapter 的实现类。将 DispatcherServlet 调用 Controller 处理请求的方法通过 HandlerAdapter 适配。

真正调用控制器方法(Controller 中的方法)的就交由 具体的处理器适配器 完成,处理器适配器需要根据传入的 映射处理器 找到与 Controller 匹配的处理器适配器。源码如下:

public class DispatcherServlet extends FrameworkServlet {

    //.............略...................

    /**
     * 处理分发到实际的处理器
     */
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //.............略...................

        // 确定当前请求的处理器适配器
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        // 真正的调用处理器,即调用控制器处理方法
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        //.............略...................
    }

    /**
     * 传入映射处理器 handler, 获取对应的处理器适配器
     */
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            // 遍历装载的所有适配器, 查找合适的适配器
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
}

HandlerAdapter 是一个处理器适配器的抽象接口,其中主要有两个方法:

public interface HandlerAdapter {

    /**
     * 处理器适配器判断给定的映射处理器是否支持
     */
    boolean supports(Object handler);

    /**
     * 给定的处理器处理请求,核心方法
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    long getLastModified(HttpServletRequest request, Object handler);
}

HandlerAdapter 有如下几个默认实现,其中后面四个在 DispatcherServlet 启动时会被初始化到 List<HandlerAdapter> handlerAdapters集合中

  • AbstractHandlerMethodAdapter
  • SimpleServletHandlerAdapter
  • HandlerFunctionAdapter
  • HttpRequestHandlerAdapter
  • RequestMappingHandlerAdapter 继承自 AbstractHandlerMethodAdapter
  • SimpleControllerHandlerAdapter

Spring AOP 中的适配器模式

Spring AOP 的实现是基于动态代里的,但 Spring AOP 的增强和通知(Advice)使用了适配器模式,与之相关的接口是 AdvisorAdapter

通知 Advice 的类型有:MethodBeforeAdviceAfterReturningAdviceThrowsAdvice

Advice 类型对应的适配器有:MethodBeforeAdviceAdapterAfterReturningAdviceAdapterThrowsAdviceAdapter

适配器结构分析:

  • :不同通知类型的处理方法(before(...), proceed(), afterReturning(...)
  • 目标:AdvisorAdapter 的抽象接口,提供判断通知类型和获取通知对应的拦截器
  • 适配器:AdvisorAdapter 的实现类,获取具体的通知拦截器交由具体的适配器完成

适配器有两个方法,一个是判断通知类型是否匹配,另一个是将通知(Advice)构建为对应的拦截器,原码如下:

public interface AdvisorAdapter {

    /**
     * 判断通知类型是否匹配
     */
    boolean supportsAdvice(Advice advice);

    /**
     * 将通知(Advice)构建为对应的拦截器
     */
    MethodInterceptor getInterceptor(Advisor advisor);
}

/**
 * 前置通知适配器
 */
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}
/**
 * 后置通知适配器
 */
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof AfterReturningAdvice);
    }
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
        return new AfterReturningAdviceInterceptor(advice);
    }
}
/**
 * 异常通知适配器
 */
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());
    }
}

默认的适配器注册实现:

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    private final List<AdvisorAdapter> adapters = new ArrayList<>(3);


    /**
     * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
     */
    public DefaultAdvisorAdapterRegistry() {
        // 创建实例时注册适配器
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }


    @Override
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // So well-known it doesn't even need an adapter.
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // Check that it is supported.
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 遍历判断找到支持通知的对应拦截器
            if (adapter.supportsAdvice(advice)) {
                // 调用适配器方法 getInterceptor
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[0]);
    }

    @Override
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }
}

示例代码

类的适配器模式结构

  1. 目标接口

    /**
     * 目标接口
     */
    public interface Target {
        void sampleOperation1();
        void sampleOperation2();
    }
    
  2. 源(需要被适配的类)

    /**
     * 源(需要适配的类)
     */
    public class Adaptee {
        public void sampleOperation1(){}
    }
    
  3. 适配器类

    一个适配器类把源(Adaptee)适配到目标(Target)中,适配器类是源的子类,可以 Override 源的一些方法。

    /**
     * 适配器类
     */
    public class Adapter extends Adaptee implements Target {
    
        /**
         * 源类没有方法sampleOperation2,
         * 因此适配器类补充上这个方法
         */
        @Override
        public void sampleOperation2() {
            //重写方法
        }    
    }
    

对象的适配器模式结构

  1. 目标接口

    /**
     * 目标接口
     */
    public interface Target {
        //源类有此方法
        void sampleOperation1();
        //源类无此方法
        void sampleOperation2();
    }
    
  2. 源(需要被适配的类)

    public class Adaptee {
        //源类有此方法
        public void sampleOperation1(){}
    }
    
  3. 适配器类

    适配器类,设置源类型属性,委托结构。

    public class Adapter implements Target {
        //源
        private Adaptee adaptee;
    
        public Adapter(Adaptee adaptee) {
            super();
            this.adaptee = adaptee;
        }
    
        @Override
        public void sampleOperation1() {
            adaptee.sampleOperation1();
        }
    
        /**
         * 源类没有此方法
         * 适配器类补充此方法
         */
        @Override
        public void sampleOperation2() {
            //重写方法
        }
    }
    

    上面的2个示例中的适配器角色补充了一个源角色没有的方法,但目标接口需要的方法。

相关参考

  1. 适配器模式

  2. 适配器模式(Adapter模式)详解

  3. 图说设计模式 - 适配器模式

  4. 设计模式 | 适配器模式及典型应用

  5. SpringMVC工作原理之适配器HandlerAdapter

  6. SpringMVC适配器模式代码示例

  7. Spring MVC之RequestMappingHandlerAdapter初始化

  8. Spring MVC之适配器的获取及执行(RequestMappingHandlerAdapter)

  9. Spring AOP(三) Advisor类架构

  10. Spring之AOP适配器模式

  11. Spring常用设计模式-适配器模式

更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: