Java适配器模式源码剖析及使用场景

文章目录

一、适配器模式介绍

适配器模式(Adapter Pattern)是一种结构型设计模式,它作用于将一个类的接口转换成客户端所期望的另一种接口,从而使原本由于接口不兼容而无法一起工作的那些类可以在一起工作。它属于包装模式的一种。

适配器模式主要分为两种:

  • 类适配器: 通过继承的方式实现适配器功能
  • 对象适配器: 通过组合的方式实现适配器功能

二、大白话理解

适配器模式可以用生活中的插座转换器来理解。我们都知道,不同国家或地区使用的插座类型不尽相同。当你从国内带着笔记本电脑出国旅行时,由于插座类型的不同,可能无法为笔记本供电。这时就需要使用一个插座转换器,将你的笔记本电源插头适配到当地的插座上。这个转换器,就是一个很好的适配器模式实例。

在这个例子中:

  • 笔记本电脑的电源插头就是"被适配者"
  • 当地的插座就是"目标接口"
  • 插座转换器就是"适配器"

三、 项目案例

假设我们有一个第三方的老式圆孔接口 LegacyRoundHole 和一个新式方形接口 NewSquarePeg。现在需要将新式方形接口适配到老式圆孔接口上。我们可以通过创建一个适配器类 SquarePegAdapter 来实现适配,代码如下:

java 复制代码
// 老式圆孔接口
interface LegacyRoundHole {
    void insertRoundPeg(RoundPeg peg);
}

// 新式方形接口
interface NewSquarePeg {
    void insertSquarePeg();
}

// 适配器(对象适配器方式)
class SquarePegAdapter implements LegacyRoundHole {
    private NewSquarePeg squarePeg;

    public SquarePegAdapter(NewSquarePeg squarePeg) {
        this.squarePeg = squarePeg;
    }

    @Override
    public void insertRoundPeg(RoundPeg peg) {
        // 通过一些算法将方形适配到圆孔
        squarePeg.insertSquarePeg();
    }
}

// 适配器(类适配器方式)
class SquarePegClassAdapter extends RoundPeg {
    private NewSquarePeg squarePeg;

    public SquarePegClassAdapter(NewSquarePeg squarePeg) {
        this.squarePeg = squarePeg;
    }

    @Override
    public void insertIntoHole(LegacyRoundHole hole) {
        // 通过一些算法将方形适配到圆孔
        squarePeg.insertSquarePeg();
    }
}

在上面的代码中,我们分别使用对象适配器和类适配器的方式实现了适配器模式。

  • SquarePegAdapter 实现了旧接口 LegacyRoundHole,同时在内部持有一个新接口 NewSquarePeg 对象。当客户端调用 insertRoundPeg 方法时,适配器就会将请求转发给新接口对象,并通过一些算法进行适配。
  • SquarePegClassAdapter 继承了 RoundPeg 类,同时在内部持有一个新接口 NewSquarePeg 对象。当客户端调用 insertIntoHole 方法时,适配器就会将请求转发给新接口对象,并通过一些算法进行适配。

这样就实现了新旧接口的兼容。

四、Java源码

在Java源码中,适配器模式也有典型的应用,比如 java.util.Arrays#asList()

java 复制代码
List<String> strs = Arrays.asList("a", "b", "c");

asList() 方法会返回一个包装好的 ArrayList 对象,里面装了传入的数组元素。这里 Arrays 类就相当于适配器,将基本的数组适配成了 List 类对象。

另一个例子是 java.io.InputStreamReaderjava.io.OutputStreamWriter:

java 复制代码
Reader reader = new InputStreamReader(new FileInputStream("file.txt"), "UTF-8");
Writer writer = new OutputStreamWriter(new FileOutputStream("file.txt"), "UTF-8");

这两个类将低级的字节流适配成高级的字符流,屏蔽了字节与字符编码转换层的底层细节。InputStreamReaderOutputStreamWriter 起到了适配器的作用。

InputStreamReader 的源码中,我们可以看到它是如何将字节流适配成字符流的:

java 复制代码
public class InputStreamReader extends Reader {
    
    private final StreamDecoder sd;

    public InputStreamReader(InputStream in, String charsetName) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
        } catch (UnsupportedEncodingException uee) {
            throw new UnsupportedCharsetException(uee.getMessage());
        }
    }

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

    // 其他方法
}

可以看到,InputStreamReader 内部持有一个 StreamDecoder 对象,它负责将字节解码成字符。当我们调用 read() 方法时,实际上是委托给 StreamDecoder 去读取和解码字节流。

适配器模式非常实用,在实际开发中经常会遇到接口不兼容的情况,通过使用适配器模式就可以让这些原本不兼容的类一起工作。

相关推荐
R.lin9 分钟前
Java 8日期时间API完全指南
java·开发语言·python
毕设源码-赖学姐15 分钟前
【开题答辩全过程】以 高校教学质量监控平台为例,包含答辩的问题和答案
java·eclipse
高山上有一只小老虎22 分钟前
翻之矩阵中的行
java·算法
西南胶带の池上桜30 分钟前
1.Pytorch模型应用(线性与非线性预测)
人工智能·pytorch·python
火钳游侠36 分钟前
java单行注释,多行注释,文档注释
java·开发语言
code bean1 小时前
【CMake】为什么需要清理 CMake 缓存文件?深入理解 CMake 生成器切换机制
java·spring·缓存
selt7911 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
RestCloud1 小时前
智能制造的底层基建:iPaaS 如何统一 ERP、MES 与 WMS 的数据流
java·wms·erp·数据传输·ipaas·mes·集成平台
夕颜1111 小时前
BeeAI 框架学习记录
后端
丘狸尾1 小时前
gradio uv无法add
开发语言·python