一、定义
The Adapter Pattern converts the interface of a class
into another interface the clients expect. Adapter lets
classes work together that couldn’t otherwise because of
incompatible interfaces.
适配器模式转换一个类的接口为客户端所需要的另一个接口。适配器让多个类协同工作,
否则会因为接口不兼容而不能工作。
适配器简单说就是转换接口,将不兼容的情况变为兼容。一个简单的例子帮助理解适配器模式,家里有三个插孔的插座,然而手机充电线是两个插孔,那么可以使用一个适配器来转换:
适配器可以分为对象适配器和类适配器,通常我们用到的都是对象适配器,类适配器只做简单介绍。
对象适配器的类图如下:
类适配器的类图:
对比两张图,很容易看出,对象适配器和类适配器的实现方式是不同的,对象适配器使用的是实现所需接口+组合实现,而类适配器使用的是多继承实现。然而,Java不支持多继承,因此,在Java中,谈论的适配器都是指对象适配器。
二、实例
(一)还记得之前的策略模式中的鸭子吗?现有一个需求,只提供了火鸡类,而客户端只能处理鸭子类,用适配器模式实现。
//客户端希望看到的接口
public interface Duck {
public void fly();
public void quack();
}
class MallardDuck implements Duck {
@Override
public void fly() {
System.out.println("I'm flying");
}
@Override
public void quack() {
System.out.println("Quack");
}
}
//实际供应的接口
interface Turkey {
public void gobble();
public void fly();
}
//实际供应的接口实现
class WildTurkey implements Turkey {
@Override
public void gobble() {
System.out.println("Gobble gobble");
}
@Override
public void fly() {
System.out.println("I'm flying a short distance");
}
}
//适配器类
class TurkeyAdapter implements Duck {
private Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
@Override
public void fly() {
//火鸡爬的慢,因此fly五次
for(int i=0;i<5;i++) {
turkey.fly();
}
}
@Override
public void quack() {
turkey.gobble();
}
}
/**
* 客户端:鸭子
* 提供端:火鸡
* 适配器:鸭子转火鸡
*/
public class DuckTest {
public static void main(String[] args) {
Turkey turkey=new WildTurkey();
Duck duckAdapter =new TurkeyAdapter(turkey);
System.out.println("The turkey says:");
duckAdapter.quack();
duckAdapter.fly();
}
}
输出结果:
The turkey says:
Gobble gobble
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
写好适配器模式的关键在于分清楚哪个是Adaptee(待转换类)、哪个是目标类,然后编写适配器类实现目标类接口,并组合Adaptee类。类图如下:
(二) Java早期版本的遍历器使用的是枚举遍历器Enumeration,支持hasMoreElements、nextElement操作,而现在的集合框架使用的是主流的Iterator,支持hasNext、next、remove操作。为了使得某些早期使用了Enumeration接口的项目不必修改代码,编写一个适配器,将Enumeration接口转为Iterator接口。
public class EnumerationAdapter<E> implements Iterator<E> {
private Enumeration<E> enumeration;
public EnumerationAdapter(Enumeration<E> enumeration) {
this.enumeration = enumeration;
}
@Override
public boolean hasNext() {
return enumeration.hasMoreElements();
}
@Override
public E next() {
return enumeration.nextElement();
}
}
类图如下:
分析:上面的适配器代码并没有实现Iterator接口的remove()方法,是因为新版本的jdk中(我用的是jdk1.8),已经把remove方法做成default类型了:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
Java的接口已经支持写实现方法了,前提是加上default修饰符。适配器模式只能支持原有的Adaptee类所支持的功能,当目标接口类要求实现的方法而Adaptee中不支持时,直接抛出操作不支持异常throw new UnsupportedOperationException();
。
三、总结
《Head First Design Patterns》一书中,还把装饰模式与适配器模式反复对比,个人觉得不大有必要,它们之间的唯一的相似性只是为类增加了一个功能,但即便如此,装饰模式是通过装饰器来新增功能,而适配器模式只是在现有的类基础上通过组合和实现接口完成接口转换工作,严格来说,适配器模式不能算新增了功能。
适配器模式很容易理解,生活中的例子也很多,各种设备、各种软件的不兼容,统统可以求助于适配器,一个转换接头、一个适配器类,就能把这类问题搞定。