适配器模式(Adapter Pattern)的官方解释:
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而
使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
很难理解官方这种说给懂的人听的解释,一般在学习设计模式时,首先疑问最大的就是,什么时候该用到这种设计模式是最合理的呢,我觉得比较直观得还是找出某项目中使用该设计模式,和不使用该设计模式的直接对比,这样理解起来就有一种茅塞顿开的感觉。
客观的解释,适配器模式,就是解决旧的接口和新的接口不兼容的问题,网上看到了一个很好的例子:
一个德国旅馆(Hotel),房间的插座都是两个孔的,一直以来都住的是德国人,德国人的手机充电器都是两个孔的,这样给手机充电就不会发生任何冲突和问题。突然有一天,酒店来了个中国人,中国人的手机充电器却是三个孔的。如果没有适配器,那酒店就得装一个三孔插座,这样就得重新装修,而且每个房间都得装,工作量巨大。于是酒店就想了一个不改变酒店的前提下(开闭原则),解决这个问题的办法:适配器模式。给每个房间装一个适配器,适配器接通两孔插座,适配器本身具有两孔和三孔的功能,就解决了此问题。
之前解释过了,适配器模式是皆苦额新旧不兼容的问题。这里的旧,指的就是德国旅店里的两孔插座,新指的是中国人需要用到的三孔插座。
组装适配器的角色:
对象适配器:
目标接口(或者抽象类)Target: 客户所期待的接口,可以是一个抽象类或接口,也可以是具体类。这里指的是德国插座,这一点解释一下,比较抽象。客户所期待的另一种接口,期待的就是德国插座能够让中国充电器冲上电的接口,但是德国插座是无法给中国充电器使用的,这里旧构成了开头所说的接口不匹配的问题。
需要适配的类(适配者)Adaptee: 需要适配的类,也就是中国插座。
适配器 Adapter:不说也懂吧,通过包装一个适配者,把原接口转换成目标接口。就是把德国插座转为中国插座。
下面用代码来说明一切:
在不使用适配器的情况下:
创建德国旅馆前一定得有德国插座:
public class DGSocket {
public void insertSocket(){
System.out.println("插入德国插座");
}
}
接着创建旅馆:
public class Hotel {
public void startCharge(DGSocket dgSocket){
dgSocket.insertSocket();
System.out.println("开始充电");
}
}
运行:
public class Test {
public static void main(String[] args) {
DGSocket dgSocket = new DGSocket();
Hotel hotel = new Hotel();
hotel.startCharge(dgSocket);
}
}
结果:
过了几年,来了个中国人,因此现在需要中国插座:
public class ZGSocket {
public void insertSocket(){
System.out.println("插入中国插座");
}
}
这下适配器就登场了:
public class SocketAdapter extends DGSocket{
private ZGSocket zgSocket;
public SocketAdapter(ZGSocket zgSocket) {
this.zgSocket = zgSocket;
}
@Override
public void insertSocket(){
zgSocket.insertSocket();
}
}
这里注意一下,不管是对象适配器还是类适配器,都需要对旧接口做继承或者实现(若就接口是一个类,像上述那样,则无法实现类适配器,应为无法在继承或者实现了,任何对象只有一个父类)。
对象适配器和类适配器的唯一区别:对象适配器是在适配器中增加一个适配者的属性,应为属性可以定义多个,所有可以适配多个适配者。但是继承只能继承一个(继承适配者类),所以类适配器无法适配多个适配者。若是类适配器,旧接口必须为接口,不然无法实现类适配器。
Test类运行:
public class Test {
public static void main(String[] args) {
ZGSocket zgSocket = new ZGSocket();
// 这一步是关键,
DGSocket dgSocket = new SocketAdapter(zgSocket);
Hotel hotel = new Hotel();
hotel.startCharge(dgSocket);
}
}
运行结果:
这就是官方所说的:从而
使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
类适配器:
若是类适配器,DGSocket 就必须为接口了:
public interface DGSocket {
// 为了区分和ZG的方法重载的问题,改了下名称
public void insertDGSocket();
}
适配器:
public class SocketAdapter extends ZGSocket implements DGSocket {
@Override
public void insertDGSocket() {
super.insertSocket();
}
}
和对象适配器类型显而易见。
Test运行:
public class Test {
public static void main(String[] args) {
SocketAdapter adapter = new SocketAdapter();
Hotel hotel = new Hotel();
hotel.startCharge(adapter);
}
}
结果:
总接:
下班了,以后在总结