Skip to content

适配器模式是结构型模式里最常用、也最容易理解的一个,平时对接第三方接口、统一日志框架时一直在用。这篇把它的三种变体(类适配器、对象适配器、接口适配器)理清楚,以及它和装饰器、代理这两个"长得像"的兄弟怎么区分。

一、解决什么问题

适配器模式解决的是**"两个接口不兼容,但要一起工作"**的问题。

最直观的类比是电源适配器:墙上是 220V 的插座,手机要的是 5V 直流电,插不进去——中间加个充电头(适配器),把 220V 转成 5V,两边就对上了。

代码里同样常见:

  • 对接第三方:对方的接口是 queryData(),你自己的系统调用的是 getList(),需要一个适配器转换
  • 统一日志:SLF4J 就是典型的适配器——它定义统一的日志接口,适配 Log4j、Logback 等不同实现
  • 兼容旧系统:新系统要调用老系统的接口,但老接口的签名对不上

一句话:适配器 = 接口的"翻译官",把一个接口转换成客户端期望的另一个接口

二、三种适配器

v1 对象适配器(组合,最常用)

java
// 目标接口:客户端期望的
public interface Target {
    void request();
}

// 被适配者:已有的、不兼容的类
public class Adaptee {
    public void specificRequest() {
        System.out.println("被适配者的方法");
    }
}

// 对象适配器:持有被适配者,转调
public class ObjectAdapter implements Target {
    private Adaptee adaptee;
    public ObjectAdapter(Adaptee adaptee) { this.adaptee = adaptee; }

    public void request() {
        adaptee.specificRequest();   // 把 request 转成 specificRequest
    }
}

// 客户端
Target target = new ObjectAdapter(new Adaptee());
target.request();

优点:通过组合持有被适配者,一个适配器能适配"被适配者及其所有子类",灵活。最推荐

v2 类适配器(继承)

java
// 类适配器:直接继承被适配者,同时实现目标接口
public class ClassAdapter extends Adaptee implements Target {
    public void request() {
        super.specificRequest();   // 直接调用继承来的方法
    }
}

缺点:Java 单继承,继承了 Adaptee 就不能再继承别的,灵活性差。能用对象适配器就别用类适配器。

v3 接口适配器(适配器模式变体)

当一个接口有很多方法,但我们只想用其中几个时,不想实现所有方法——用一个"空实现"的中间类兜底:

java
// 原接口:方法太多
public interface MouseListener {
    void click();
    void doubleClick();
    void rightClick();
    void hover();
}

// 接口适配器:空实现所有方法
public abstract class MouseAdapter implements MouseListener {
    public void click() {}
    public void doubleClick() {}
    public void rightClick() {}
    public void hover() {}
}

// 使用方:只重写自己关心的方法
MouseListener listener = new MouseAdapter() {
    public void click() { System.out.println("点击了"); }
};

这就是"接口适配器/缺省适配器"——用一个空实现类,让使用方只重写关心的方法

适配器模式结构:客户端通过适配器,把目标接口转成被适配者的方法

三、实际应用

  • SLF4J:统一日志门面,slf4j-api 定义接口,slf4j-log4j12/logback-classic 是适配器,把 SLF4J 接口转到具体日志实现
  • JDBC 驱动Driver 把统一的 JDBC 接口适配到各家数据库的私有协议
  • 旧系统对接:老接口 getData() 包装成新系统要的 fetchList()

小结

  • 适配器解决"接口不兼容",是接口的"翻译官"
  • 三种:对象适配器(组合,推荐)、类适配器(继承,受单继承限制)、接口适配器(空实现兜底,只重写关心的方法)
  • 典型:SLF4J 统一日志、JDBC 驱动、第三方对接
  • 与装饰器/代理的区别:适配器是"接口转换"(两个不同接口),装饰器是"功能增强"(同一接口),代理是"访问控制"(同一接口)

想区分"同一接口下的增强与控制",接着看《装饰器模式》和《代理模式》(文章整理中)。