结构型模式--适配器模式

适配器模式概述

如果去欧洲国家去旅游的话,他们的插座如下图最左边,是欧洲标准,而我们使用的插头如下图最右边的。因此我们的笔记本电脑,手机在当地不能直接充电。所以就需要一个插座转换器,转换器第1面插当地的插座,第2面供我们充电,这样使得我们的插头在当地可以使用。

生活中这样的例子很多,手机充电器(将220v转换为5v的电压),读卡器等,其实就是使用到了适配器模式。

 

 定义

将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度更高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对比较少。

结构

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

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

类适配器模式

实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。

【例】读卡器

现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式,创建一个读卡器,将TF卡的内容读出来

类图如下:

 代码如下

//sd卡接口
public interface SDCard {

    String readSD();

    void writeSD(String msg);
    
}
//sd卡业务
public class SDCardImpl implements SDCard {
    @Override
    public String readSD() {
        return "SDCard read msg";
    }

    @Override
    public void writeSD(String msg) {
        System.out.println("SDCard write " + msg);
    }
}
//TF的接口
public interface TFCard {

    //从TF卡中读取数据
    String readTF();

    //往TF卡中写数据
    void writeTF(String msg);

}
//TF卡的业务
public class TFCardImpl implements TFCard {

    @Override
    public String readTF() {
        return "TFCard read msg";
    }

    @Override
    public void writeTF(String msg) {
        System.out.println("TFCard write msg");
    }
}
//电脑
public class Computer {

    public String readSD(SDCard sdCard) {
        return sdCard.readSD();
    }
}

//客户端
    public static void main(String[] args) {
        Computer computer = new Computer();
        String s = computer.readSD(new SDCardImpl());
        System.out.println(s);
    }

 

 

 这样电脑只能读取SD卡,无法读取别的卡,加入适配器类

//适配器类
public class SDAdapterTF extends TFCardImpl implements SDCard {

    @Override
    public String readSD() {
        System.out.println("adapter read tf card");
        return readTF();
    }

    @Override
    public void writeSD(String msg) {
        System.out.println("adapter write tf card");
        writeTF(msg);
    }
}
public static void main(String[] args) {
Computer computer = new Computer();
String s = computer.readSD(new SDCardImpl());
System.out.println(s);

System.out.println("=================");
//使用该电脑读取TF卡
String s1 = computer.readSD(new SDAdapterTF());
System.out.println(s1);
}
 

 

 

 对象适配器模式

  1. 基本思路和类的适配器模式相同,只是将Adapter类做修改,不是继承src类,而是持有src类的实例,以解决兼容性问题。即:持有src类,实现dst类的接口,完成src->dst的适配
  2. 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系
  3. 对象适配器模式是适配器模式常用的一种

对象适配器模式应用实例

 

 

 

 

 

代码实现:适配器不在继承被适配者,而是改为持有

public class SDAdapterTF implements SDCard {

    //声明适配者类
    private TFCard tfCard;

    public SDAdapterTF(TFCard tfCard) {
        this.tfCard = tfCard;
    }

    @Override
    public String readSD() {
        System.out.println("adapter read tf card");
        return tfCard.readTF();
    }

    @Override
    public void writeSD(String msg) {
        System.out.println("adapter write tf card");
        tfCard.writeTF(msg);
    }
}

    public static void main(String[] args) {
        Computer computer = new Computer();
        String s = computer.readSD(new SDCardImpl());
        System.out.println(s);

        System.out.println("=================");
        SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl());
        String s1 = computer.readSD(sdAdapterTF);
        System.out.println(s1);
    }

 

 

对象适配器模式的注意事项和细节

  1. 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以他解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口
  2. 使用成本更低,更灵活

接口适配器模式

如果直接实现一个接口的话,要重写所有的方法,我们用一个抽象类去实现接口,对方法默认实现,然后我们创建这个抽象类的实例,重写我们要的方法即可

代码如下

接口:

public interface Interface1 {

    void m1();

    void m2();

    void m3();

    String m4();
}

 

使用抽象类默认实现接口.

public abstract class AbsAdapter implements Interface1{

    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public String m4() {
        return null;
    }
}

 

客户端调用接口的方法,

    public static void main(String[] args) {
        AbsAdapter absAdapter = new AbsAdapter() {
            @Override
            public String m4() {
                return "使用了m4的方法";
            }
        };
        System.out.println(absAdapter.m4());
    }

 

 

 注意:还有一个适配器模式就是接口适配器模式,当不希望实现一个接口中的所有方法时,可以创建一个抽象类Adapter,实现所有方法。而此时我们只需要继承该抽闲即可

引用场景

  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致
  • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同

JDK源码解析

Reader(字符流),inputStream(字节流)的适配使用的就是InputStreamReaderOutputStreamWriter

InputStreamReader和OutputStreamWriter分别继承自java.io包中的Reader和Writer,对他们中的抽象的未实现的方法给出实现。如:

 适配器模式在springMVC框架应用的使用

SpringMVC中的HandlerAdapter,就使用了适配器模式,SpringMVC处理请求的流程回顾:

  1. 用户发送请求,由SpringMVC中的dispatcherServlet 的 doDispatch() 方法统一接受处理
  2. 在该方法中,通过HandlerMapping,找到用户请求的路径对应的Handler处理器,也就是你的Controller方法
  3. 根据获取的Handler不同,在获取到此Handler对象的适配器
  4. 再对通过适配器进行调用,实际Handler的处理方法,返回ModelAndView对象
  5. 返回得ModelAndView 通过 InternalResourceViewResolve 进行处理解析,有可能是某个页面,也可能是一个json字符串,都会以字符串形式返回给前端
  6. 前端根据接口的字符串进行解析

总结

在上面的描述中,当我们调用实际的Handler时,使用了适配器模式,这样我们调用不同类型的Handler,在使用方却可以使用同一种方式调用,使用方和逻辑处理方充分解耦,便于扩展

源码

  1. 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;
                        //通过mapperHandler 获取该请求路径的Handler
                        mappedHandler = this.getHandler(processedRequest);
                        if (mappedHandler == null) {
                            this.noHandlerFound(processedRequest, response);
                            return;
                        }
                        //根据此时Handler 的类型 获取对应的适配器
                        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
    
                        //后面就无需知道具体的Handler类型,通过统一的适配器调用
                        String method = request.getMethod();
                        boolean isGet = HttpMethod.GET.matches(method);
                        if (isGet || HttpMethod.HEAD.matches(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);
                }
    
            }
        }

     

 

适配器模式的注意事项和细节

  1. 三种命名方式是根据src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的
  2. 类适配器:以类给到,在Adapter里,就是将src当做类,继承
  3. 对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
  4. 接口适配器,以接口给到,在Adapter里,将src作为一个接口,实现
  5. Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作
  6. 实际开发中,实现起来不拘泥与我们所讲的三种经典形式

 

posted @ 2022-09-16 10:19  着迷JAVA  阅读(96)  评论(0)    收藏  举报