适配器相信大家都见过,我们使用的电脑的电源插头就会有一个适配器,在交流电和电脑之间做一个桥接和电流转换;如果你要去国外,国外的插座和你的电器的插头不匹配,但是你还是想使用自己的电器,怎么办呢?这就可以使用适配器,在国内的插头与国外的插座之间做桥接,达到电器使用的目的。换句话说,就是给电器的接口转化成了另一个接口。来,我们看定义:适配器模式是将一个类的接口,转换成客户期望的另一个接口,让原来不兼容的类可以合作无间。
现在我们回到适配器模式上,为什么需要适配器模式呢?假如我们有一个软件系统,需要和其他厂商的软件系统搭配使用,两个系统都已经设计好了,但是暴露出来的api不一致,这时候我们不愿意修改原来的代码,当然厂商的代码更是无法修改(修改权在厂商手中,它们是api的定义者,当然它们觉得,第三方使用它们的产品,当然得按照它们的接口定义来),这时候我们该怎么办呢?大家应该可以想到,跟现实生活的例子结合在一起就很明白,插头和插口不一致,而两者又不可变,那么久可以做出来一个适配器使得插头和插口完美结合在一起,同样的,若两个软件系统的调用方和被调用方不匹配,我们也可以做出一个适配器类来衔接两者。
我们再换个立场来思考,假如我们的软件系统被别人调用,但是我们需要修改接口,但又不希望希望一一通知我们的客户让他们修改,即如果我们要做到客户对我们的修改无感知,那么就需要适配器类,这可以让客户从实现的接口解耦,适配器可以将改变的部门封装起来,这样客户就不必为了应付不同的接口而每次跟着修改。
下面来看例子:
/**
* 华为手机
*/
public class HuaweiPhone {
ChinaSocket socket;
public HuaweiPhone(ChinaSocket socket){
this.socket = socket;
}
public void gainElec() {
socket.charge();
}
}
/**
* 中国插座
*/
public interface ChinaSocket {
public void charge220V();
}
/**
* 公牛
*/
public class Bull implements ChinaSocket{
public void charge220V() {
System.out.println("用220V的电压充电");
}
}
我们有一个华为手机,可以通过输出电压为220V中国标准的公牛插座充电,完全ok。
/**
* 有一部华为手机和一个公牛插座
*/
public class ChinaMain {
public static void main(String[] args) {
ChinaSocket socket = new Bull();
HuaweiPhone huaweiPhone = new HuaweiPhone(socket);
huaweiPhone.gainElec();
}
}
运行结果:
用220V的电压充电
Process finished with exit code 0
但是假如现在我们在欧洲,我们有华为手机和欧洲插座abc
**
* 华为手机
*/
public class HuaweiPhone {
ChinaSocket socket;
public HuaweiPhone(ChinaSocket socket){
this.socket = socket;
}
public void gainElec() {
socket.charge220V();
}
}
/**
* 欧洲插座
*/
public interface EuropeSocket {
public void charge110V();
}
/**
* 欧洲某一品牌的插座
*/
public class abc implements EuropeSocket {
public void charge110V() {
System.out.println("用110V电压充电");
}
}
这样肯定是不能使用的。。
现在我们想给华为手机充电,就可以找一个适配器
/**
* 适配器
*/
public class SocketAdapter implements ChinaSocket{
EuropeSocket eSocket;
public SocketAdapter(EuropeSocket eSocket){
this.eSocket = eSocket;
}
public void charge220V() {
System.out.println("下面是欧洲的充电标准");
eSocket.charge110V();
System.out.println("将欧洲标准转化为中国标准:220V");
}
}
该适配器对外暴露的是中国标准,而内部包装的是欧洲标准,适配器可以对已有的电压进行转换,通过它,我们就可以给华为手机充电了,如下:
/**
* Created by anxufeng on 2019/5/22
*/
public class Main {
public static void main(String[] args) {
EuropeSocket eSocket = new abc(); //有一欧洲标准的abc品牌插座
ChinaSocket socket = new SocketAdapter(eSocket); //适配器对欧洲插座包装并转换电压
HuaweiPhone huaweiPhone = new HuaweiPhone(socket); //华为手机可以用适配器充电
huaweiPhone.gainElec();
}
}
运行结果:
下面是欧洲的充电标准
用110V电压充电
将欧洲标准转化为中国标准:220V
Process finished with exit code 0
适配器模式使用与原来的两个系统的代码已经完成,并想将甲乙两个系统配合使用,但是定义的api不一致,又无法改变或不想改变原来代码就可以使用的情况。若是在开发过程中或开发前,那么调用方完全可以按照被调用方的接口设计来开发,而避免使用适配器模式。
可能很多同学对装饰器模式和适配器模式比较混淆,它们的区别是这样的:装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),适配器模式则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。