代码改变世界

实用指南:【设计模式】适配器模式 在java中的应用

2025-09-25 13:45  tlnshuju  阅读(9)  评论(0)    收藏  举报

适配器模式简介

适配器模式(Adapter Pattern),又称为包装器模式,是一种结构型设计模式。它的核心作用是**“将一个接口转换成客户希望的另一个接口”**,使原本因接口不兼容而无法工作的类可以协同工作。

为什么需要适配器模式?

在实际开发中,我们经常会遇到这样的问题:
我们手头有一个类A(比如老版本的接口),而现在需要用它来适配到一个新接口B(比如新系统的标准)。直接修改A的代码可能不现实(比如A是第三方库),这时候就可以用适配器模式来“桥接”它们。

类比

假设你买了一台美国进口的笔记本电脑,它的电源插头是美标的两扁头,而中国大陆的插座是国标的三孔插座。
你不能直接把美标插头插进国标插座,这时就需要一个插头转换器
插头转换器的作用就是:一边兼容美标插头,一边兼容国标插座,让两个本来“不兼容”的东西可以协同工作。

适配器模式的结构与角色

适配器模式的结构其实很简单,主要包括以下几个角色:

目标接口(Target)

这是客户端期待的接口,也就是系统希望使用的标准接口。
举例:你家里的国标插座就是目标接口。

package com.example.adapter;
public interface Target {
void request();
}

需要适配的类(Adaptee)

这个类是已经存在的,接口不兼容但功能可用。
举例:美标插头的设备就是 Adaptee。

package com.example.adapter;
public class Adaptee
{
public void specificRequest() {
System.out.println("Adaptee: 处理特殊请求");
}
}

适配器(Adapter)

适配器是连接目标接口和需要适配的类的“桥梁”。
它实现了目标接口,并在内部持有 Adaptee 的引用,通过转换让二者协同工作。
举例:插头转换器就是 Adapter。

package com.example.adapter;
// 对象适配器实现方式
public class Adapter
implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 在这里做转换,让 Adaptee 能被 Target 使用
adaptee.specificRequest();
}
}

结构图示意
+-----------+         +------------+         +-----------+        +-----------+
|  Client   |  --->   |  Target    |    | Adaptee   |
+-----------+         +------------+         +-----------+        +-----------+
  • Client 通过 Target 接口调用 Adapter
  • Adapter 内部调用 Adaptee 的方法,实现接口转换

小结:
适配器模式的核心结构就是:目标接口(Target)+ 需要适配的类(Adaptee)+ 适配器(Adapter)
适配器实现了目标接口,并通过组合或继承的方式调用 Adaptee 的功能,实现接口的兼容。

适配器模式的实现方式

在 Java 中,适配器模式有两种常见实现方式:类适配器对象适配器
这两种方式本质上都是让“不兼容”的类能用统一的接口,但实现细节略有不同。


类适配器实现(基于继承)

类适配器通过继承需要适配的类(Adaptee),并实现目标接口(Target)。
由于 Java 只支持单继承,所以类适配器模式有一定局限性。

结构示意:

package com.example.adapter;
// 目标接口
public interface Target {
void request();
}
// 需要适配的类
public class Adaptee
{
public void specificRequest() {
System.out.println("Adaptee: 处理特殊请求");
}
}
// 类适配器
public class ClassAdapter
extends Adaptee implements Target {
@Override
public void request() {
// 直接调用父类方法
specificRequest();
}
}

使用示例:

package com.example.adapter;
public class AdapterDemo
{
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
// 输出:Adaptee: 处理特殊请求
}
}

特点:

  • 适配器通过继承 Adaptee 实现接口转换。
  • 只能适配一个类(受限于 Java 的单继承)。

对象适配器实现(基于组合)

对象适配器通过组合的方式,在适配器内部持有 Adaptee 的实例,并实现目标接口(Target)。

结构示意:

package com.example.adapter;
// 目标接口
public interface Target {
void request();
}
// 需要适配的类
public class Adaptee
{
public void specificRequest() {
System.out.println("Adaptee: 处理特殊请求");
}
}
// 对象适配器
public class ObjectAdapter
implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}

使用示例:

package com.example.adapter;
public class AdapterDemo
{
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
// 输出:Adaptee: 处理特殊请求
}
}

特点:

  • 适配器通过组合 Adaptee 实例实现接口转换。
  • 可以适配多个类(更灵活,推荐使用)。

小结:

  • 类适配器:继承 Adaptee,适用于单继承场景,结构简单但灵活性较低。
  • 对象适配器:组合 Adaptee,更常用,灵活性高,推荐实际开发中优先选择。

适配器模式的实际应用场景

适配器模式在实际开发中非常常见,尤其是在系统集成、旧接口兼容、新旧系统迁移等场景。下面用具体例子和类比,帮助你理解适配器模式的实际应用。


集成第三方库

场景说明:
假设你在项目中需要用到一个第三方的日志库(如 Log4j),但你的系统统一使用了自定义的 Logger 接口。如果直接改动系统代码成本太高,可以为第三方库写一个适配器,让它实现你的 Logger 接口。

代码示例:

package com.example.adapter;
// 系统要求的日志接口
public interface Logger {
void log(String message);
}
// 第三方日志库
import org.apache.log4j.Logger as Log4jLogger;
public class Log4jAdapter
implements Logger {
private Log4jLogger log4jLogger;
public Log4jAdapter(Log4jLogger log4jLogger) {
this.log4jLogger = log4jLogger;
}
@Override
public void log(String message) {
log4jLogger.info(message);
// 转换到第三方库的方法
}
}

数据库驱动兼容

场景说明:
假如你的系统原本用的是 MySQL 数据库,后来需要支持 PostgreSQL,但你的 DAO 层接口是统一的。可以写一个适配器,把 PostgreSQL 的驱动方法适配成你的 DAO 接口。


旧系统迁移/升级

场景说明:
公司老系统的接口和新系统不兼容,但又不能一下子全部重写。可以用适配器模式,将新系统的接口适配到老系统的实现,让二者能共同运行。


前端开发中的兼容处理

场景说明:
比如你用 Vue3 编写新组件,但项目里还有很多 Vue2 的代码,可以通过适配器模式包装 Vue2 组件,让它们可以在 Vue3 环境下运行。


JDK 中的应用

场景说明:
Java 标准库里也大量使用适配器模式。例如 java.io.InputStreamReader 适配了 InputStreamReader,让字节流可以作为字符流处理。

源码片段:

// InputStreamReader.java
public class InputStreamReader
extends Reader {
private final InputStream in;
public int read(char[] cbuf, int offset, int length) throws IOException {
// 实现将字节流转换为字符流
}
}

适配器模式的优缺点分析

优点

提高代码复用性
可以复用已有的功能类(Adaptee),不需要修改原有代码,只需通过适配器进行接口转换。

增强系统灵活性和扩展性
适配器模式让系统能够兼容更多的第三方类或旧系统,便于后期扩展和集成。

解耦系统各模块
客户端只依赖目标接口(Target),不关心具体实现细节,有利于降低模块间耦合度。

满足“开放-封闭原则”
通过新增适配器类来适配不同接口,无需修改原有代码,符合 OCP 原则。


缺点

增加系统复杂度
引入适配器后,系统类的数量增加,结构变得更加复杂,理解和维护成本提升。

适配器编写成本
每当有新的 Adaptee 需要适配时,都要编写新的适配器类,增加开发工作量。

可能影响性能
适配器模式通常会增加一次方法调用(间接层),在高性能要求场景下可能略有影响。

只能适配“接口”不兼容,不能解决“业务”不兼容
适配器只能转换接口,无法解决业务逻辑上的差异,如果 Adaptee 的业务与 Target 差异过大,适配器模式就不适用了。