定义:
将一个类的接口转换成客户期望的另一个接口,从而使得原本因为接口不兼容而无法一起工作的两个类可以在一起工作。
设计类图:
适配器中的角色:
- 目标(Target)角色:客户端所期望的目标接口。
- 源(Adapee)角色:实际需要适配的对象源接口。
- 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。适配器必须是具体类。
示例代码
public interface Target {
/**目标接口方法*/
public void request();
}
public class Adaptee {
/**需要进行适配的方法*/
public void specificRequest(){
System.out.println("原始的方法");
}
}
/**适配器*/
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
super.specificRequest();
}
}
public class Client {
public static void main(String[] args) {
//创建一个适配器对象, 当成Target接口来使用
Target target = new Adapter();
//实际会调用到Adaptee的方法
target.request();
}
}
这种适配器模式也叫作类适配器,在上述代码中Adapter类同实现了Target接口,同时继承了Adaptee类,因此Adapter可以同时当成Target和Adaptee来用,在调用Adapter的目标方法的时候,Adapter再去调用Adaptee的方法,这样就过渡的实现一个接口转换的过程。Adapter就像一个媒介,使得Target和Adaptee可以互通有无。
在上述代码中Target必须是一个接口,而不能是类,但是如果是在C++代码中实现,那么Adapter就可以实现多继承,Target也可以是一个类。类图就变成下面这样子:
适配器模式还有一种叫做对象适配器,与上面的类适配器基本相同,区别是对象适配器中的Adapter不是通过继承Adaptee,而是通过组合的方式来实现。它的类图如下:
示例代码:
/**适配器*/
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
this.adaptee.specificRequest();
}
}
public class Client {
public static void main(String[] args) {
//创建一个适配器对象, 当成Target接口来使用
Target target = new Adapter(new Adaptee());
//实际会调用到Adaptee的方法
target.request();
}
}
可以看到Adapter由继承Adaptee变成了持有Adaptee的对象,这种适配器模式遵循了一个良好的OO原则:多用组合少用继承。同时它还有另外一个优点,那就是被适配者的任何子类,都可以搭配着适配器使用。在这种模式里面Adaptee也可以是一个接口。
对象适配器VS类适配器
不得不承认,对象适配器相比类适配器而言有更好的优势,但是类适配器本身也有对象适配器无法替代的功能,那就是类适配器可以覆盖或重新定义父类的方法。对于这两种适配器模式的取舍,要根据实际需求来定,但是一般情况下,多用组合的方式要灵活于继承的方式
图解适配器
适配器模式是一个神奇而强大的模式,它为我们实现了一种似乎不可能完成的任务。在《Head First设计模式》当中有几张图很形象的表达了适配器模式到底干了什么,我们来看一下:
电源的插座转换器,我相信没有什么比这个更形象的表达了,啥也不用说,看一眼就明白了。
![](https://i-blog.csdnimg.cn/blog_migrate/b3247bd2cc608d601cbf9430c8f5578b.jpeg)
上图,把尖的插进圆的?貌似不可能,但加入适配器,你会发现OMG! Nothing Is Impossible:
加入适配器以后,一切都变得天衣无缝:
相信你已经深刻理解了,没错,中间充当适配的组件,它具有两个最大的特点,那就是同时具备源接口和目标接口的特性,只有这样它才能同时融合源和目标,这就是适配器的魅力所在。
适配器使用场景
- 当你需要使用一个类,但你发现它的接口不符合系统的预期时,可以使用适配器转换。
- 当你想要扩展一个类的方法,想要它变成另外的方法时,可以使用适配器转换。
参考: