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

    • 什么是适配器模式?

    先从一个例子说起:你有一台Type-c接口的手机,某天你突然想买条高贵的Sony耳机体验是什么感觉。然后行兴高采烈买回来才发现,这个耳机的插头竟然是见鬼的3.5mm插头,你心里一顿***。此时你没钱再买一条耳机,更没有钱换一台手机。怎么办?能想到的办法就是买一条转接线,这条转接线为这两种类型的接口做了一个适配,使手机能把声音正确的输出到耳机当中。

    由例子可以看出:适配器模式是为了解决两个不同的对象之间不兼容的问题,通过一个设配器的中间对象,而使双方能在一起配合工作。在上面例子中,耳机的3.5mm接口可以被当作被适配者(Adaptee),手机的Type-c接口可以被当作目标接口(Target),而那条转接线就是适配器(Adapter)。

    • 适配器模式代码展示

    在这里我们再以笔记本的适配器为例,我们知道中国家用电压为220V,而笔记本的输入电压一般不会超过20V。这时,我们也需要一个电源适配器使得我们的笔记本能够正常工作。

    首先,确定各个物件的身份。电源适配器:Adapter   家用电源:Adaptee   笔记本设备接口:Target

     1 /**
     2  * @author HILL
     3  * @version V1.0
     4  * @date 2019/7/8
     5  **/
     6 public class HousePower implements Power{
     7 
     8     /**
     9      * 中国家庭用电电压为220伏
    10      * @Author: HILL
    11      * @date: 2019/7/8 15:07
    12     **/
    13     private static final int VOLTAGE = 220;
    14 
    15     /**
    16      * @Author: HILL
    17      * @date: 2019/7/8 15:08
    18      * @return: int 中国家庭用电电压
    19     **/
    20     @Override
    21     public int outPutPower() {
    22         return VOLTAGE;
    23     }
    24 }
     1 public interface Adapter {
     2 
     3     /**
     4      * 每个适配器有特定的转换电压
     5      * @Author: HILL
     6      * @date: 2019/7/9 14:18
     7      * @param: []
     8      * @return: int 返回特定的转换电压
     9     **/
    10     int outConversionVoltage();
    11 }
    笔记本需要的接口:Target

    在这里,我们采用的是类适配器,即通过继承的方式完成两者之间的适配,这种方法没那么灵活,但是在编码方面比较方便快捷。

    /**
     * @author HILL
     * @version V1.0
     * @date 2019/7/8
     **/
    public class LapTopAdapter extends HousePower implements Adapter{
    
    
        /**
         * TODO
         * @Author: HILL
         * @date: 2019/7/9 14:06
         * @param: []
         * @return: int 输出适配器转换后的电压值
        **/
        @Override
        public int outConversionVoltage() {
            //进行电压转换
            System.out.println("电压为"+super.outPutPower()+"开始转换");
            return super.outPutPower()/11;
        }
    }

    很明显,上面的代码关系是这样的,我们的笔记本设备就可以通过Adapter接口完成电源的接入,成功运行。

     1 /**
     2  * @author HILL
     3  * @version V1.0
     4  * @date 2019/7/7
     5  *  TODO
     6  **/
     7 public class LapTop implements Device{
     8 
     9     private Adapter adapter ;
    10     private int VOLTAGE = 20;
    11 
    12     public LapTop(Adapter adapter) {
    13         this.adapter = adapter;
    14     }
    15 
    16     @Override
    17     public void startUse() {
    18         if(adapter.outConversionVoltage()==VOLTAGE){
    19             System.out.println("电压为"+VOLTAGE+"-----笔记本启动");
    20         }else{
    21             System.out.println("电压输入不正确,启动失败。异常电压为:"+adapter.outConversionVoltage());
    22         }
    23     }
    24 }
    笔记本调用
    • 为什么要用适配器模式(优点)?

    结论:

    1. 有利于Target与Adaptee的解耦,通过适配器能减少两者的关联性,那么在Adaptee做出修改的时候,我们只需要更换适配器,而不需要直接修改Target。
    2. 提供代码的复用性,既然可以简单的通过适配器就能完成功能,为何还要再造轮子?例如:既然能通过电源适配器转换电压,为何还要国家电网再供应20v的电压?
    3. 灵活易扩展,这里也是利用到了多态的特性,我们可以很灵活的更换一个适配器。这个自然可以理解。

    在上面的例子中,可能利于理解适配器模式,但是不利于在开发中有自己的体会。所以接下来请看开发中框架用到的适配器模式。

    • 框架源码中的适配器模式

    1. SpringMVC中的适配器模式

    我们可以看到springmvc的执行流程是如下图所示,至于具体的流程就不细说了,相信大家也懂。

    在这个流程图中,为什么DispatcherServlet不直接调用controller获取返回的结果,而选择在这两者间添加一个适配器(Adapter)呢?来看看SpringMVC的文档怎么说。有兴趣的可以自己去研究:https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web.html#mvc-servlet-special-bean-types

    这里我们直接贴出最重要的一句话。


    Help the DispatcherServlet to invoke a handler mapped to a request, regardless of how the handler is actually invoked.
    For example, invoking an annotated controller requires resolving annotations.
    The main purpose of a HandlerAdapter is to shield the DispatcherServlet from such details.

    HandlerAdapter最主要的目的是对DispatcherServlet屏蔽细节,然后让适配器实现具体细节,而DispatcherServlet是需要调用接口即可,不需要频繁的修改代码。试想一下,如果多出一个controller你就要添加一个调用的判断,那得多么的麻烦。

     1 //dispatcherServlet中的执行方法
     2   HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
     3 //执行方法,返回视图
     4  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
     5 
     6 
     7 //假设直接调用
     8 if(controller  == LoginController){
     9      //do sth   
    10 }else if(controller == orderController){
    11      //do sth  
    12 }else if(...)

    其实还有很多框架也用到了适配器模式,尤其在安卓的开发当中。但是由于自己水平有限,博客就写到这里。

  • 相关阅读:
    你知道require是什么吗?
    jQuery类库的设计
    多线程下载图片
    多线程与CPU和多线程与GIL
    一个python小爬虫
    一个方格表的问题
    使用django发布带图片的网页(上)
    uWSGI+Django+nginx(下)
    uWSGI+Django (中)
    Linux下安装Python3的django并配置mysql作为django默认数据库(转载)
  • 原文地址:https://www.cnblogs.com/hill1126/p/11158752.html
Copyright © 2011-2022 走看看