1. 概述

在本篇文章中,我们将深入探讨适配器模式(Adapter Pattern),包括它的变体、在 Java 中的应用场景以及如何实现它。

适配器模式的作用就像一个“转接头”,用于连接两个原本不兼容的接口。它的核心目标是将一个现有接口转换为客户期望的另一个接口

这个模式的结构与 装饰器模式 有些相似,但两者的使用场景不同。装饰器模式通常是在设计之初就考虑扩展性而引入的,而适配器模式则常常是在代码已经写好之后,为了兼容已有接口而加入的。

适配器模式有两种主要实现方式,我们接下来会逐一讲解。


2. 适配器模式详解

2.1. 对象适配器(Object Adapter)

对象适配器通过组合(Composition)来委托逻辑到适配器中,这种方式实现起来非常简单直观:

Adapter

在这个结构中,Adapter 类持有 Adaptee 的实例,并将 request() 方法委托给 AdapteespecificRequest() 方法。

这种方式非常灵活,而且不依赖继承,适用于大多数场景。


2.2. 类适配器(Class Adapter)

⚠️ 类适配器需要多重继承的支持,这在 Java 中是无法直接实现的(除非使用接口的 default 方法)。

其核心思想是让 Adapter 同时继承 TargetAdaptee。不过,在 Java 中我们可以变通一下:如果 Target 是一个接口,我们就可以通过实现该接口并继承 Adaptee 来模拟类适配器的行为

Adapter Extending Target

从图中可以看出,这种方式与对象适配器很像,但 Adapter 是通过继承而不是组合来持有 Adaptee 的。

✅ 优点是:这个 Adapter 可以同时作为 TargetAdaptee 使用,也就是所谓的双向适配器,在某些场景下非常实用。


2.3. 优缺点对比

类适配器最适合在 TargetAdaptee 的方法是一对一映射的情况下使用。此时我们可以通过委托来实现,而不需要在 Adapter 中添加额外逻辑。

❌ 但如果 Target 接口比较复杂,类适配器就需要额外实现很多方法。这时可以通过组合 + 委托来解决:

Concrete Target

在这个例子中,我们只委托了 request()Adaptee,其余方法则由 ConcreteTarget 提供实现。这样可以避免重复造轮子。

如果不需要双向适配器,✅ 对象适配器会是更简单的选择:

Client

📌 总结来说,适配器模式的实现方式取决于以下几点:

  • 代码库的初始结构
  • 是否能使用接口
  • 是否需要双向适配

3. 适配器模式实战:Java 中的 Enumeration 与 Iterator

Java 标准库中就有一个非常经典的适配器模式案例:EnumerationIterator

3.1. Enumeration 接口

先来看 Enumeration 接口:

public interface Enumeration<E> {
    
    boolean hasMoreElements();

    E nextElement();

    default Iterator<E> asIterator() {
        return new Iterator<>() {
            @Override public boolean hasNext() {
                return hasMoreElements();
            }
            @Override public E next() {
                return nextElement();
            }
        };
    }
    
}

3.2. Iterator 接口

再来看 Iterator 接口的定义(官方文档):

Iterator 是对集合的迭代器,它取代了 Java 集合框架中的 Enumeration。Iterator 和 Enumeration 的区别在于:

  • Iterator 允许在迭代过程中删除元素,并且语义明确
  • 方法命名更合理

从接口定义来看,IteratorEnumeration 功能相似,只是方法名不同:

public interface Iterator<E> {

    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
    
}

3.3. 适配器的具体实现

从 Java 9 开始,Enumeration 接口新增了一个默认方法 asIterator(),它返回一个 Iterator 实例,这就是一个典型的匿名类实现的适配器模式

default Iterator<E> asIterator() {
    return new Iterator<>() {
        @Override public boolean hasNext() {
            return hasMoreElements();
        }
        @Override public E next() {
            return nextElement();
        }
    };
}

这种实现方式使用了组合的思想,虽然没有显式传入 Enumeration 实例,但因为是在 Enumeration 内部创建 Iterator,所以可以直接访问其方法。

✅ 这是一种非常强大的技巧,可以在不暴露接口的前提下,通过委托来实现适配。

但如果我们要在 Java 9 之前实现类似的功能,就需要手动写适配器类。比如对象适配器的方式:

public class IteratorAdapter<E> implements Iterator<E> {
    
    private Enumeration<E> enumeration;
    
    public IteratorAdapter(Enumeration<E> enumeration) {
        this.enumeration = enumeration;
    }
    
    @Override
    public boolean hasNext() {
        return enumeration.hasMoreElements();
    }
    
    @Override
    public E next() {
        return enumeration.nextElement();
    }

}

这与我们前面讲的对象适配器完全一致。

再来看类适配器的例子。我们以 StringTokenizer 为例,它实现了 Enumeration 接口:

public class StringTokenizerIteratorAdapter extends StringTokenizer implements Iterator<String> {

    public StringTokenizerIteratorAdapter(final String str, final String delim, final boolean returnDelims) {
        super(str, delim, returnDelims);
    }

    public StringTokenizerIteratorAdapter(final String str, final String delim) {
        super(str, delim);
    }

    public StringTokenizerIteratorAdapter(final String str) {
        super(str);
    }

    @Override
    public boolean hasNext() {
        return hasMoreTokens();
    }

    @Override
    public String next() {
        return nextToken();
    }
}

✅ 这样我们就创建了一个双向适配器,它既是一个 Iterator,也是一个 StringTokenizerIterator 的方法委托给了 StringTokenizer 中更具体的方法。


4. 总结

在这篇文章中,我们深入讲解了 Java 中的适配器模式,包括它的两种实现方式:对象适配器和类适配器。

适配器模式是管理代码复杂度、与遗留系统交互、复用第三方库时非常重要的设计模式之一。它让我们能够在不修改原有代码的前提下,轻松实现接口的兼容。

📌 完整示例代码可以在 GitHub 上找到。


适配器模式虽小,但用处极大,特别是在大型项目中,是每个 Java 工程师都应该掌握的经典技巧之一。


原始标题:The Adapter Pattern in Java