设计模式十–适配器模式

这是我参与更文挑战的第23天,活动详情查看: 更文挑战

设计模式

适配器模式

适配器模式就是将某个类的接口转换成客户端期望的另一个接口,比如我们的耳机都是圆的,而手机只有方的充电口,那么我们怎么使用耳机?这时候转接头就是适配器,将方口的接口转换成圆口的接口。

类适配器

比如我们的手机的耳机口是方口的

Phone

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public class Phone {
    public String rectanglePort() {
        String src = "方口";
        System.out.println(src + "的耳机口");
        return src;
    }

}
复制代码

而我们的耳机需要的是圆口的,我们有一个圆形的接口

RoundPort

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public interface RoundPort {
    public String roundPort();
}
复制代码

所以我们的适配器

PhoneAdapter

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public class PhoneAdapter extends Phone implements RoundPort {

    @Override
    public String roundPort() {
        String src = rectanglePort();
        if (src.equals("方口")) {
            //复杂的转换过程之后
            src = "圆口";
        }
        return src;
    }
}
复制代码

我们的耳机有一个听歌的功能,此时像实现听歌,就必须依赖圆口

HeadPhone

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public class HeadPhone {
    public void listen(RoundPort roundPort) {
        if (roundPort.roundPort().equals("圆口")) {
            System.out.println("正在听歌");
        } else {
            System.out.println("无法连通");
        }
    }
}
复制代码

main

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public class Adapter {
    public static void main(String[] args) {
        HeadPhone headPhone = new HeadPhone();
        headPhone.listen(new PhoneAdapter());
    }
}
复制代码

此时,我们实现这个适配器必须继承Phone来使用rectanglePort这个方法,此时Phone的所有的方法都将暴露给Adapter,增加了使用成本,又因为Java是单继承,所以RoundPort必须是接口。

对象适配器

根据“合成复用原则”,将继承关系转换成关联关系。对象适配器也是最常用的适配器模式。

所以只需要修改PhoneAdapter为

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:46
 */
public class PhoneAdapter1 implements RoundPort {
    private Phone phone;

    public PhoneAdapter1(Phone phone) {
        this.phone = phone;
    }

    @Override
    public String roundPort() {
        if (phone != null) {
            String src = phone.rectanglePort();
            if (src.equals("方口")) {
                src = "圆口";
            }
            return src;
        }
        return null;
    }
}
复制代码

main

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public class Adapter {
    public static void main(String[] args) {
        HeadPhone headPhone = new HeadPhone();
        headPhone.listen(new PhoneAdapter1(new Phone()));
    }
}
复制代码

这种方式不仅更加灵活,也可结成成本。

接口适配器

也称为缺省适配器模式,当不需要全部实现接口提供的方法时可以使用这个模式,不需要的方法没只需要实现空方法。

提供一个万能的适配器。

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.21 13:23
 */
public interface UniversalAdapter {
    String roundPort();

    String rectanglePort();

    String squarePort();
}
复制代码

让我们的适配器去默认实现空方法,待使用的时候重写

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:46
 */
public class PhoneAdapter2 implements UniversalAdapter {
    private Phone phone;

    public PhoneAdapter2(Phone phone) {
        this.phone = phone;
    }

    @Override
    public String roundPort() {
        return phone.rectanglePort();
    }

    @Override
    public String rectanglePort() {
        return phone.rectanglePort();
    }

    @Override
    public String squarePort() {
        return phone.rectanglePort();
    }
}
复制代码

使用时重写

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 13:44
 */
public class HeadPhone1 {
    public void listen() {

        PhoneAdapter2 phoneAdapter2 = new PhoneAdapter2(new Phone()) {
            @Override
            public String roundPort() {
                if (super.roundPort().equals("方口")) {
                    String src = "圆口";
                    return src;
                }
                return null;
            }
        };
        if (phoneAdapter2.roundPort().equals("圆口")) {
            System.out.println("听音乐");
        } else {
            System.out.println("连通失败");
        }
    }
}

复制代码

main

package com.wangscaler.adapter;

/**
 * @author wangscaler
 * @date 2021.06.25 11:06
 */
public class Adapter {
    public static void main(String[] args) {
        HeadPhone1 headPhone = new HeadPhone1();
        headPhone.listen();
    }
}
复制代码

此种方式易于扩展,比如我们有方口的耳机,也可以直接重写squarePort来直接实现。这种方式在安卓的开发中是比较常见的。

源码中的建造者模式

SpringMVC中的HandlerAdapter

public class DispatcherServlet extends FrameworkServlet {
 	java@Nullable
    private List<HandlerMapping> handlerMappings;
    @Nullable
    private List<HandlerAdapter> handlerAdapters;
     public DispatcherServlet() {
        this.setDispatchOptionsRequest(true);
    }

    public DispatcherServlet(WebApplicationContext webApplicationContext) {
        super(webApplicationContext);
        this.setDispatchOptionsRequest(true);
    }
    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");
    }
 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);
            }

        }
    }
复制代码

当我们发起请求的时候,比如login请求,就会经过DispatcherServlet,在doDispatch中,根据mappedHandler = this.getHandler(processedRequest);,从请求中获取到login这个handler。然后根据HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());他会去在getHandlerAdapter中迭代找到适合的适配器。

适配器HandlerAdapter源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}
复制代码

从下图可以看到,有6个类实现了我们的接口

image.png

然后通过执行 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());来执行我们的Handler,执行完我们的业务接口之后,会拿到我们返回的ModelAndView,然后去寻找对应的view资源,进行返回。

在这里DispatcherServlet作为用户,HandlerAdapter 就是我们期望接口

总结

适配器不是在详细设计时添加的,而是解决正在服役的项目的问题,现在的接口不适合使用,可以利用适配器模式有动机地修改一个正常运行的系统的接口编程我们期待的接口。

使用适配器会增加系统的复杂性,增加代码阅读难度,过多使用适配器会使系统代码变得凌乱,所以尽量避免使用适配器。

核心就是”把一个类的接口变换成客户端所期待的另一种接口

参考资料

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享