1. 概述
在本篇文章中,我们将深入探讨适配器模式(Adapter Pattern),包括它的变体、在 Java 中的应用场景以及如何实现它。
适配器模式的作用就像一个“转接头”,用于连接两个原本不兼容的接口。它的核心目标是将一个现有接口转换为客户期望的另一个接口。
这个模式的结构与 装饰器模式 有些相似,但两者的使用场景不同。装饰器模式通常是在设计之初就考虑扩展性而引入的,而适配器模式则常常是在代码已经写好之后,为了兼容已有接口而加入的。
适配器模式有两种主要实现方式,我们接下来会逐一讲解。
2. 适配器模式详解
2.1. 对象适配器(Object Adapter)
✅ 对象适配器通过组合(Composition)来委托逻辑到适配器中,这种方式实现起来非常简单直观:
在这个结构中,Adapter
类持有 Adaptee
的实例,并将 request()
方法委托给 Adaptee
的 specificRequest()
方法。
这种方式非常灵活,而且不依赖继承,适用于大多数场景。
2.2. 类适配器(Class Adapter)
⚠️ 类适配器需要多重继承的支持,这在 Java 中是无法直接实现的(除非使用接口的 default
方法)。
其核心思想是让 Adapter
同时继承 Target
和 Adaptee
。不过,在 Java 中我们可以变通一下:如果 Target
是一个接口,我们就可以通过实现该接口并继承 Adaptee
来模拟类适配器的行为:
从图中可以看出,这种方式与对象适配器很像,但 Adapter
是通过继承而不是组合来持有 Adaptee
的。
✅ 优点是:这个 Adapter
可以同时作为 Target
和 Adaptee
使用,也就是所谓的双向适配器,在某些场景下非常实用。
2.3. 优缺点对比
✅ 类适配器最适合在 Target
和 Adaptee
的方法是一对一映射的情况下使用。此时我们可以通过委托来实现,而不需要在 Adapter
中添加额外逻辑。
❌ 但如果 Target
接口比较复杂,类适配器就需要额外实现很多方法。这时可以通过组合 + 委托来解决:
在这个例子中,我们只委托了 request()
到 Adaptee
,其余方法则由 ConcreteTarget
提供实现。这样可以避免重复造轮子。
如果不需要双向适配器,✅ 对象适配器会是更简单的选择:
📌 总结来说,适配器模式的实现方式取决于以下几点:
- 代码库的初始结构
- 是否能使用接口
- 是否需要双向适配
3. 适配器模式实战:Java 中的 Enumeration 与 Iterator
Java 标准库中就有一个非常经典的适配器模式案例:Enumeration
和 Iterator
。
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 允许在迭代过程中删除元素,并且语义明确
- 方法命名更合理
从接口定义来看,Iterator
和 Enumeration
功能相似,只是方法名不同:
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
,也是一个 StringTokenizer
。Iterator
的方法委托给了 StringTokenizer
中更具体的方法。
4. 总结
在这篇文章中,我们深入讲解了 Java 中的适配器模式,包括它的两种实现方式:对象适配器和类适配器。
适配器模式是管理代码复杂度、与遗留系统交互、复用第三方库时非常重要的设计模式之一。它让我们能够在不修改原有代码的前提下,轻松实现接口的兼容。
📌 完整示例代码可以在 GitHub 上找到。
适配器模式虽小,但用处极大,特别是在大型项目中,是每个 Java 工程师都应该掌握的经典技巧之一。 ✅