zoukankan      html  css  js  c++  java
  • 适配器模式

    一、定义

    适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

    这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

    适配器模式(Adapter)包含以下主要角色。

    1. 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
    2. 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
    3. 适配器(Adapter)类:它是一个转换器,通过继承Adaptee 或 实现Target接口,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

    二、适配器模式的应用场景

    最实际的生活按例就是电压的转接,比喻手机充电器,把家用电转成了低压转出;如果是在生产环境中,比喻说把一个不适合调用者的接口方法,通过适配器转成一个适合用户调用的接口或方法,再说白点,例如登录模式的扩展,原系统可能只有密码登录,现在要扩展成QQ、微信、电话,这时就可以用适配器进行补救,不改原代码进行扩展

    创建Target接口(期待得到的插头):能输出110V(将220V转换成110V)

    //Target接口
    public interface Target {
        //将220V转换输出110V(原有插头(Adaptee)没有的)
        public void Convert_110v();
    }
    //适配者(Adaptee)
    public class PowerPort220V {
        //原有插头只能输出220V
        public int Output_220v(){
            int voltage=220;
            System.out.println("原有插头只能输出220V");
            return voltage;
        }
    }
    //适配器(Adapter)
    public class Adapter220V  extends PowerPort220V implements Target{
        //适配器在内部转换
        @Override
        public void Convert_110v() {
            int voltage=this.Output_220v();
            System.out.println("拿到了原电压,下面开始转压");
            System.out.println("转接后的电压是"+voltage/2);
        }
    }
    public class AdapterPattern {
        public static void main(String[] args){
    
            Target mAdapter220V = new Adapter220V();
            //用户拿着充电器插上适配器(调用Convert_110v()方法)
            //再将适配器插上原有插头(Convert_110v()方法内部调用Output_220v()方法输出220V)
            //适配器只是个外壳,对外提供110V,但本质还是220V进行供电
            mAdapter220V.Convert_110v();
    
        }
    }

    上面的类适配器虽然实现了适配器的功能,但是他违背了最少知道原则,因为在适配器类中可以调用到原类中的逻辑代码,所以为解决这个问题引出了下一种适合器:对象适配器;对象适配器的原理就是通过组合来实现适配器功能,具体做法就是让Adapter实现Target接口,然后内部持有Adapter实例,然后Target接口规定的方法内转换Adapter;

    代码还是上面代码,只改适配器Adapter实现,其它的不用改

    //Target接口
    public interface Target {
        //将220V转换输出110V(原有插头(Adaptee)没有的)
        public void Convert_110v();
    }
    //适配器(Adapter)
    public class Adapter220V implements Target {
    
        private PowerPort220V powerPort220V;
    
        public Adapter220V(PowerPort220V powerPort220V){
            this.powerPort220V=powerPort220V;
        }
        //适配器在内部转换
        @Override
        public void Convert_110v() {
            int voltage=powerPort220V.Output_220v();
            System.out.println("拿到了原电压,下面开始转压");
            System.out.println("转接后的电压是"+voltage/2);
        }
    }
    //适配者(Adaptee)
    public class PowerPort220V {
        //原有插头只能输出220V
        public int Output_220v(){
            int voltage=220;
            System.out.println("原有插头只能输出220V");
            return voltage;
        }
    }
    public class AdapterPattern {
        public static void main(String[] args){
    
            Target mAdapter220V = new Adapter220V(new PowerPort220V());
            //用户拿着充电器插上适配器(调用Convert_110v()方法)
            //再将适配器插上原有插头(Convert_110v()方法内部调用Output_220v()方法输出220V)
            //适配器只是个外壳,对外提供110V,但本质还是220V进行供电
            mAdapter220V.Convert_110v();
    
        }
    }

    上面对象适配器虽然解了开闭原则,但是还是不完美的,当接口过多时,前面两种适配器就无法解决空实现的问题,这时引出了另一个适配器:接口适配器;接口适配器的关注点与类适配器和对象适配器的关注点不一样,类适配器和对象适配器着重于将系统存在的一个角色转化成目标接口所需内容,而接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,类显得很臃肿。此时,使用接口适配器就能让我们只实现我们需要的接口方法,目标更明确

    //Target接口
    public interface Target {
        //将220V转换输出110V(原有插头(Adaptee)没有的)
        public void Convert_110v();
    
        public void Convert_10v();
    }
    //适配器(Adapter)
    public class Adapter220V implements Target {
    
        private PowerPort220V powerPort220V;
    
        public Adapter220V(PowerPort220V powerPort220V){
            this.powerPort220V=powerPort220V;
        }
        //适配器在内部转换
        @Override
        public void Convert_110v() {
            int voltage=powerPort220V.Output_220v();
            System.out.println("拿到了原电压,下面开始转压");
            System.out.println("转接后的电压是"+voltage/2);
        }
    
        @Override
        public void Convert_10v() {
    
        }
    }
    //适配者(Adaptee)
    public class PowerPort220V {
        //原有插头只能输出220V
        public int Output_220v(){
            int voltage=220;
            System.out.println("原有插头只能输出220V");
            return voltage;
        }
    }
    public class AdapterPattern {
        public static void main(String[] args){
    
            Target mAdapter220V = new Adapter220V(new PowerPort220V());
            //用户拿着充电器插上适配器(调用Convert_110v()方法)
            //再将适配器插上原有插头(Convert_110v()方法内部调用Output_220v()方法输出220V)
            //适配器只是个外壳,对外提供110V,但本质还是220V进行供电
            mAdapter220V.Convert_110v();
    
        }
    }

    三、源码应用

    spring中适配器模式也应用得非常多,例如在springAop中的AdvisorAdapter类,它有三个实现类MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter,

    package org.springframework.aop.framework.adapter;
    
    import org.aopalliance.aop.Advice;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.springframework.aop.Advisor;
    
    public interface AdvisorAdapter {
        boolean supportsAdvice(Advice var1);
    
        MethodInterceptor getInterceptor(Advisor var1);
    }
    package org.springframework.aop.framework.adapter;
    
    import java.io.Serializable;
    import org.aopalliance.aop.Advice;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.MethodBeforeAdvice;
    
    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
        MethodBeforeAdviceAdapter() {
        }
    
        public boolean supportsAdvice(Advice advice) {
            return advice instanceof MethodBeforeAdvice;
        }
    
        public MethodInterceptor getInterceptor(Advisor advisor) {
            MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
            return new MethodBeforeAdviceInterceptor(advice);
        }
    }
    package org.springframework.aop.framework.adapter;
    
    import java.io.Serializable;
    import org.aopalliance.aop.Advice;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.AfterReturningAdvice;
    
    class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
        AfterReturningAdviceAdapter() {
        }
    
        public boolean supportsAdvice(Advice advice) {
            return advice instanceof AfterReturningAdvice;
        }
    
        public MethodInterceptor getInterceptor(Advisor advisor) {
            AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice();
            return new AfterReturningAdviceInterceptor(advice);
        }
    }
    package org.springframework.aop.framework.adapter;
    
    import java.io.Serializable;
    import org.aopalliance.aop.Advice;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.ThrowsAdvice;
    
    class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
        ThrowsAdviceAdapter() {
        }
    
        public boolean supportsAdvice(Advice advice) {
            return advice instanceof ThrowsAdvice;
        }
    
        public MethodInterceptor getInterceptor(Advisor advisor) {
            return new ThrowsAdviceInterceptor(advisor.getAdvice());
        }
    }

    Spring 会根据不同的 AOP 配置来确定使用对应的 Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice;其实在SpringMVC中的 HandlerAdapter 类也用到了适配器

     其适配调用的关键代码还是在 DispatcherServlet 的 doDispatch()方法中

     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                try {
                    ModelAndView mv = null;
                    Object dispatchException = null;
    
                    try {
                        processedRequest = this.checkMultipart(request);
                        multipartRequestParsed = processedRequest != request;
                        mappedHandler = this.getHandler(processedRequest);
                        if (mappedHandler == null) {
                            this.noHandlerFound(processedRequest, response);
                            return;
                        }
    
                        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                        String method = request.getMethod();
                        boolean isGet = "GET".equals(method);
                        if (isGet || "HEAD".equals(method)) {
                            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                            if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                                return;
                            }
                        }
    
                        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                            return;
                        }
    
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return;
                        }
    
                        this.applyDefaultViewName(processedRequest, mv);
                        mappedHandler.applyPostHandle(processedRequest, response, mv);
                    } catch (Exception var20) {
                        dispatchException = var20;
                    } catch (Throwable var21) {
                        dispatchException = new NestedServletException("Handler dispatch failed", var21);
                    }
    
                    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
                } catch (Exception var22) {
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
                } catch (Throwable var23) {
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
                }
    
            } finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                } else if (multipartRequestParsed) {
                    this.cleanupMultipart(processedRequest);
                }
    
            }
        }

    在 doDispatch()方法中调用了 getHandlerAdapter()方法

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            if (this.handlerAdapters != null) {
                Iterator var2 = this.handlerAdapters.iterator();
    
                while(var2.hasNext()) {
                    HandlerAdapter adapter = (HandlerAdapter)var2.next();
                    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");
        }

    在 getHandlerAdapter()方法中循环调用了 supports()方法判断是否兼容,循环迭代集合中的 Adapter 又是在初始化时早已赋值。

    四、总结

    优点:
    1、能提高类的透明性和复用,现有的类复用但不需要改变。
    2、目标类和适配器类解耦,提高程序的扩展性。
    3、在很多业务场景中符合开闭原则。
    缺点:
    1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。
    2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

    3、适配器是一个后期补救方法,是设计考虑不周的补救

    git源码::https://github.com/ljx958720/design_patterns.git

    这短短的一生我们最终都会失去,不妨大胆一点,爱一个人,攀一座山,追一个梦
  • 相关阅读:
    从输入网址到页面呈现的过程
    Git 常用命令合集
    Jquery浅克隆与深克隆
    CSS变量教程
    设计模式
    Servlet和JSP简述
    SQL Server,MySQL,Oracle三者的区别
    mysql事务处理
    计时器
    java中length,length(),size()区别
  • 原文地址:https://www.cnblogs.com/xing1/p/14585541.html
Copyright © 2011-2022 走看看