本文示例代码材料源自Head First设计模式
以前整理自己整理的链接:
https://blog.csdn.net/u011109881/article/details/58261956
思想
要了解适配器模式,就得先了解什么是适配器,百度百科的定义如下:
适配器是一个接口转换器,它可以是一个独立的硬件接口设备,允许硬件或电子接口与其它硬件或电子接口相连,也可以是信息接口。比如:电源适配器、三角架基座转接部件、USB与串口的转接设备等。
适配器看上去好像不太好懂,那么我说另一个名词:转接口呢?个人觉得,适配器模式的原理就和转接口差不多,比如普通的耳机无法连接iPhone,但是加根转接线就好了,普通的USB线无法给type C接口的手机充电,但是加个转接口就好啦。说到底,适配器就是当某个东西不满足当前条件时,我们通过一个转换对象,使得这个东西满足条件。我觉得Head First设计模式的图解还是很生动的。
情景再现
现在我们有鸭子类的接口和具体实现类如下:
public interface Duck {
public void quack();
public void fly();
}
public class MollardDuck implements Duck{
public void quack() {
System.out.println("MollardDuck quack");
}
public void fly() {
System.out.println("MollardDuck flying");
}
}
我们还有另外一个物种,火鸡及其实现类
public interface Turkey {
public void gobble();
public void fly();
}
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("WildTurkey goggle");
}
public void fly() {
System.out.println("WildTurkey fly");
}
}
现在假设我们缺少鸭子对象,要拿火鸡来充数。(因为鸭子和火鸡还是存在很多类似的部分的,两个大相径庭的事物是不能使用适配器的。)要如何做呢?
那就得实现一个适配器。
public class TurkeyToDuckAdapter implements Duck{
Turkey turkey;
public TurkeyToDuckAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack() {
turkey.gobble();
}
public void fly() {
//由于火鸡飞的很近,要像鸭子飞的那么远要飞五次,所以这里需要调用5次
for(int i =0;i<5;i++){
turkey.fly();
}
}
}
适配器或者转接口会生效么?先看看测试类:
public class Test {
public static void main(String[] args) {
List<Duck> ducks = new ArrayList<Duck>();
Duck mllardDuck = new MollardDuck();
ducks.add(mllardDuck);
Turkey wildTurkey = new WildTurkey();
Duck wildTurkeyDuck = new TurkeyToDuckAdapter(wildTurkey);
ducks.add(wildTurkeyDuck);
Iterator<Duck> iterator = ducks.iterator();
while(iterator.hasNext()){
testDuck(iterator.next());
}
}
static void testDuck(Duck duck){
duck.fly();
duck.quack();
}
}
ducks的列表中是无法加入turkey的,因为泛型规定,只能加入Duck类型的物种。但是有了适配器或者转接口,就可以把火鸡伪装成鸭子,加入鸭子的列表。甚至瞒过testDuck方法的眼睛。
测试结果:
MollardDuck flying
MollardDuck quack
WildTurkey fly
WildTurkey fly
WildTurkey fly
WildTurkey fly
WildTurkey fly
WildTurkey goggle
几点拓展
适配器的工作量:
增加一个适配的工作量和适配对象的复杂度成正比,比如鸭子和火鸡,本身都很简单,因此适配很简单。但试想一下,如果两者都很复杂,比如Duck接口有20几个方法,那么适配器势必都要实现这20个方法,因此复杂度和接口复杂度成正比
适配器可以封装多个类么:
我觉得可以通过实现多个接口来实现。
装饰器和适配器对比:
两者都是将对象进行封装,只不过目的不同。装饰器的包装是为了给原先的对象添加一种新的功能。而适配器的目的是将一个对象包装成像另一种对象的样子,以达到符合接口要求的目的。
类适配器和对象适配器
主要区别就是实现方式类适配器通过继承实现,而对象适配器通过实现接口实现。本文中的例子就是一个对象适配器。如果将Duck由接口改成class,那么适配器就会变成类适配器。书中列出了许多两者的区别,但个人觉,他们的区别并不是很大。