标签
适配器模式是一种结构型设计模式,通过适配器将一个类的接口转换成客户端期望的另一种接口,使得不兼容的接口可以一起工作。它通常在希望复用现有类但接口不匹配时使用。
定义
适配器模式通过创建一个中间类(适配器)连接目标接口和现有接口,使得两个接口可以协同工作。适配器既可以是类适配器(基于继承)也可以是对象适配器(基于组合)。
使用场景
- 系统需要使用现有的类,但该类的接口不符合需求。
- 需要通过统一的接口调用多个不同的接口。
- 希望封装和解耦不同模块之间的依赖关系。
结构
适配器模式包括以下角色:
- 目标接口(Target):定义客户端期望的接口。
- 适配者类(Adaptee):现有接口,需要适配的类。
- 适配器类(Adapter):实现目标接口,并通过适配者类完成目标接口所需的功能。
类图
代码示例
假设有一个音频播放器
AudioPlayer
只能播放 MP3 文件。为了支持播放更丰富的格式(如 MP4 和 VLC),我们使用适配器模式。1. 目标接口
2. 适配者类
3. 适配器类
4. 客户端类
JDK 17 实现
在 JDK 17 中,可以使用**密封类(sealed class)**来定义
MediaPlayer
和 AdvancedMediaPlayer
,以明确限制允许的适配器类,确保设计的安全性。JDK源码解析
Reader(字符流)、InputStream(字节流)的适配使用的是InputStreamReader。
InputStreamReader继承自java.io包中的Reader,对他中的抽象的未实现的方法给出实现。如:
如上代码中的sd(StreamDecoder类对象),在Sun的JDK实现中,实际的方法实现是对sun.nio.cs.StreamDecoder类的同名方法的调用封装。类结构图如下:
从上图可以看出:
- InputStreamReader是对同样实现了Reader的StreamDecoder的封装。
- StreamDecoder不是Java SE API中的内容,是Sun JDK给出的自身实现。但我们知道他们对构造方法中的字节流类(InputStream)进行封装,并通过该类进行了字节流和字符流之间的解码转换。
结论:
从表层来看,InputStreamReader做了InputStream字节流类到Reader字符流之间的转换。而从如上Sun JDK中的实现类关系结构中可以看出,是StreamDecoder的设计实现在实际上采用了适配器模式。
注意事项
- 兼容性问题:适配器模式主要是为了兼容不匹配的接口,但可能导致适配器类较多,增加维护难度。
- 接口稳定性:适配器的设计应尽量保证接口稳定,避免频繁修改目标接口。
- 性能问题:使用适配器可能会增加额外的调用层次,尤其在频繁调用的场景下,可能导致性能下降。
扩展
- 对象适配器与类适配器:
- 对象适配器:通过组合的方式持有适配者类的引用,适合需要适配多个类的情况。
- 类适配器:通过继承适配者类实现适配,适合简单适配场景,但受限于 Java 单继承。
- 双向适配器:实现两种接口,使得两个不兼容的模块可以相互调用。例如,可以设计一个适配器同时实现
MediaPlayer
和AdvancedMediaPlayer
,实现双向适配。
- 应用场景:
- 数据转换:如 XML 和 JSON 的相互适配。
- 库或框架升级:通过适配器兼容新旧接口。
- 系统集成:对接不同厂商的 API 接口。
小结
适配器模式通过一个中间类实现接口的转换和兼容,使得不同接口的类可以协同工作。在实际应用中,应根据需求选择合适的适配器类型(对象适配器或类适配器)。适配器模式虽然增加了系统灵活性,但也可能引入额外的复杂度,尤其是在适配器数量较多时,应注意代码维护性和性能问题。