前言
前面讲的装饰者模式是将对象包装起来,并赋予新的职责。适配器模式同样是包装对象,但是目的不一样,它要让某些对象的接口看起来不像自己而是像别的东西。为什么要这样做,因为可以将类的接口转换成想要的接口。还会讲一个适配器的变种模式:外观模式,它将对象包装起来以简化其接口。
正文
1、现实世界的适配器模式
《Head First设计模式》给了一个很好的案例,就是我们如果想使用各国不同制式的插座,就需要一个电源适配器。在对象的世界里,适配器模式往往用于匹配旧系统和新需求之间的gap的。
将上面的图示解释成适配器模式就是下面这个图:
- 客户依据目标接口调用适配器的方法对适配器发出请求;
- 适配器使用被适配者的接口,将请求转换成被适配者的一个或多个接口;
- 客户收到返回的结果,但并未察觉这一切是适配器在起转换作用。
2、适配器模式定义
适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器模式可以让原本不兼容的类可以合作。
适配器模式有很好的OO设计原则:使用对象组合。现在来看它的类图:
3、适配器模式案例
旧世界的枚举器:Java早期的集合如Vector、Stack都实现了一个elements()方法,该方法返回一个Enumeration接口类型,这个接口可以逐一遍历集合的元素,而无需知道它们在集合内是如何被管理的。
public interface Enumeration<E> {
boolean hasMoreElements();
E nextElement();
}
新世界的迭代器:当Java推出新的集合后,开始应用Iterator迭代器(迭代器模式后面再讲)接口,这个接口和枚举接口很像,都可以遍历集合中的每个元素,只不过Iterator接口还提供了删除元素的能力。
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
开发者作为客户,经常面对历史遗留代码,就像上面老容器的遍历。客户希望使用新的Iterator迭代器的接口规格来遍历老的集合。于是可以写一个适配器来适配新标准和老功能。我们来分析角色,开发者对应上图的Client,新标准接口Iterator就是Target,老功能Enumeration就是被适配者Adaptee。还缺一个重要角色:适配者Adapter。Java维护者或者开发者自己可以写一个适配器,这个适配器需要实现新标准接口Iterator,并组合老功能接口Enumeration。
实现代码如下:
public class EnumerationIterator implements Iterator {
private Enumeration enumeration;
public EnumerationIterator(Enumeration enumeration) {
this.enumeration = enumeration;
}
@Override
public boolean hasNext() {
return enumeration.hasMoreElements();
}
@Override
public Object next() {
return enumeration.nextElement();
}
@Override
public void remove() {
// 目标类不支持的动作无能为力
throw new UnsupportedOperationException();
}
}
4、适配器vs装饰器
两者都是组合对象,但是两个模式的目的不同,装饰器组合对象后在原有功能之上增强功能,适配器组合对象后编排原有功能使之能适配新接口标准。
5、外观模式
学习了前面后,我们知道适配器模式是将一个类的接口转换成另一个符合客户期望的接口。我们再来看一个改变接口的新模式,叫外观模式,它将一个或多个类的复杂实现都隐藏,只暴露一个干净的外观。可以将外观模式看做适配器模式的扩展。
外观模式提供了一个统一的接口,用来访问子系统的一群接口。
6、外观模式案例
家庭影院包含很多系统:DVD播放器、投影机、升降屏幕、音响等。每个系统都有自己的操作菜单,如果我们想要观赏一部电影,需要依次调用上面系统类的多个方法,非常复杂。于是我们创建一个家庭影院类,用它来包装各个子系统的复杂实现。代码其实很简单:
public class HomeTheaterFacade {
// 组合各个子系统
Amplifier amp;
Tuner tuner;
DvdPlayer player;
...
// 外观接口
public void watchMovie() {
// 编排接口
amp.on();
amp.setDvd(dvd);
amp.setVolum(5);
dvd.on();
dvd.play(movie);
}
}
7、最少知识原则
最少知识原则:只和你的密友谈话
这个原则希望我们再设计系统时,不要让太多的类耦合在一起,免得修改系统的一部分,会影响到其他部分。怎么避免呢?该原则给了一些方针:就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
- 该对象本身;
- 被当做方法参数而传递进来的对象;
- 此方法内创建的对象;
- 组合使用的对象;
举个例子,如果我们想从气象站获得当前温度,我们只需要知道气象站对象就够了,不需要再认识气象站本身依赖的其他对象。
// 不采用此原则
getTemp() {
// 这里我们从气象站获得了温度计对象Thermometer,再获得温度
Thermometer ther = station.getThermometer();
return ther.getTemperture();
}
// 采用此原则
getTemp() {
// 应用此原则后,在气象站中加一个获取温度方法,减少类的依赖
return station.getTemperture();
}
总结
适配器模式组合对象,改变老功能行为以适配新的接口标准;
外观模式组合对象,提供简洁的外观接口,隐藏复杂的子系统。