思考适配器模式
适配器模式通过转换已有的接口,达成目标需要的接口
适配器模式还可以将多种差异化接口适配成同一接口做统一输出
1.适配器模式的本质
适配器模式的本质是:转换匹配,复用功能。
适配器通过转换调用已有的实现,从而能把已有的实现匹配成需要的接口,使之能满足客户端的需要。也就是说转换匹配是手段,而复用已有的功能才是目的。
在进行转换匹配的过程中,适配器还可以在转换调用的前后实现一些功能处理,也就是实现智能的适配。
2.何时选用适配器模式
建议在以下情况中选用适配器模式。
-
在系统开发过程中,可能会遇到需要使用第三方组件或库,但是这些组件或库的接口与当前系统的接口不兼容的情况。此时可以使用适配器模式,将第三方组件或库的接口转换成当前系统的接口,以便能够方便地集成和使用。
-
当需要重用一个类,但是其接口与其他类不兼容时,也可以使用适配器模式。通过创建一个适配器来实现两个类的接口兼容,以便能够重用该类。
3.优缺点
适配器模式有如下优点。
-
适配器模式可以让原本不兼容的接口之间能够协同工作,提高代码的重用性和灵活性。
-
适配器模式可以让客户端代码与具体类实现解耦,提高代码的可维护性和可扩展性。
-
适配器模式可以隐藏系统的实现细节,提高系统的安全性和稳定性。
适配器模式有如下缺点。
- 过多地使用适配器,会让系统非常零乱,不容易整体进行把握
比如,明明看到调用的是A接口,其实内部被适配成了B接口来实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
4.适配器模式的结构
- Client:客户端,调用自己需要的领域接口 Target。
- Target:定义客户端需要的跟特定领域相关的接口。
- Adaptee:已经存在的接口,通常能满足客户端的功能要求,但是接口与客户端要求的特定领域接口不一致,需要被适配。
- Adapter:适配器,把Adaptee适配成为Client需要的Target。
5.实现
对象适配
原理:通过组合来实现适配器功能。
对象适配器是指使用一个对象作为中间适配器,将一个接口转换成另一个接口。在对象适配器中,适配器持有一个被适配对象的引用,并将客户端的请求转发给被适配对象来实现适配。
模拟实现多种支付,现系统对接了微信支付和支付宝支付
/**
* @description:支付接口
*/
public interface IPay {
void pay(String orderId);
}
/**
* @description:微信支付
*/
public class WeiXinPay implements IPay{
@Override
public void pay(String orderId) {
System.out.println("调用微信支付接口,订单id:"+orderId);
}
}
/**
* @description:支付宝支付
*/
public class ZhiFuBaoPay implements IPay{
@Override
public void pay(String orderId) {
System.out.println("调用支付宝支付接口,订单id:"+orderId);
}
}
现加入银联支付,想通过pay()统一调用所有的支付方法,需要进行适配
/**
* @description:银联支付
*/
public class YinLianPay{
/**
* 银联支付的方法名及参数同微信支付宝的支付方法不一样
*/
public void orderPay(String orderId, String name) {
System.out.println("调用银联支付接口,订单id:" + orderId);
}
}
/**
* @description:银联支付适配器
*/
public class YinLianAdapter implements IPay {
//持有银联支付对象
private YinLianPay yinLianPay;
public YinLianAdapter(YinLianPay yinLianPay) {
this.yinLianPay = yinLianPay;
}
@Override
public void pay(String orderId) {
System.out.println("调用银联支付适配器");
yinLianPay.orderPay(orderId, "张三");
}
}
测试类
public class PayClient {
public static void main(String[] args) {
//微信支付
IPay weiXinPay=new WeiXinPay();
weiXinPay.pay("10001");
//支付宝支付
IPay zhiFuBaoPay=new ZhiFuBaoPay();
zhiFuBaoPay.pay("10002");
//银联支付
IPay yinLianPay=new YinLianAdapter(new YinLianPay());
yinLianPay.pay("10003");
}
}
结果
调用微信支付接口,订单id:10001
调用支付宝支付接口,订单id:10002
调用银联支付适配器
调用银联支付接口,订单id:10003
接口适配
原理:通过抽象类来实现适配,这种适配稍别于上面所述的适配。
接口适配器是指使用一个抽象类作为中间适配器,将一个接口转换成另一个接口。在接口适配器中,适配器实现了目标接口,并对目标接口的所有方法进行了空实现。然后,客户端只需要实现目标接口中需要的方法即可,不需要实现所有方法。
当存在这样一个接口,其中定义了N多的方法,而我们现在却只想使用其中的一个到几个方法,如果我们直接实现接口,那么我们要对所有的方法进行实现,哪怕我们仅仅是对不需要的方法进行置空(只写一对大括号,不做具体方法实现)也会导致这个类变得臃肿,调用也不方便,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们在创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。
/**
* @description:A接口
*/
public interface A {
void a();
void b();
void c();
void d();
void e();
void f();
}
/**
* @description:适配器,实现A接口但不实现
*/
public abstract class Adapter implements A {
public void a(){}
public void b(){}
public void c(){}
public void d(){}
public void e(){}
public void f(){}
}
/**
* @description:重写需要的部分方法
*/
public class RealA extends Adapter{
@Override
public void a() {
System.out.println("实现a方法");
}
@Override
public void b() {
System.out.println("实现b方法");
}
}
public class AClient {
public static void main(String[] args) {
A realA = new RealA();
//只有a,b方法实现了
realA.a();
realA.b();
//c,d,e未实现
realA.c();
realA.d();
realA.e();
}
}
结果
实现a方法
实现b方法
类适配
原理:通过继承来实现适配器功能。
当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类BB,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用BB中的合适方法,这样就完成了一个简单的类适配器。
手机充电口是typec,充电宝是usb口,使用转接器转接
/**
* @description:usb接口
*/
public interface Usb {
/**
* 使用usb
*/
void useUsb();
}
/**
* @description:TypeC接口
*/
public interface TypeC {
/**
* 使用TypeC
*/
void useTypeC();
}
/**
* @description:usb实现类
*/
public class Usber implements Usb{
@Override
public void useUsb() {
System.out.println("使用USB接口");
}
}
适配器
/**
* @description:TypeC适配器
*/
public class TypeCAdapter extends Usber implements TypeC{
@Override
public void useTypeC() {
//将typc接口转换成usb接口
useUsb();
}
}
测试类
public class TypeCClient {
public static void main(String[] args) {
TypeC typeC = new TypeCAdapter();
typeC.useTypeC();
}
}
结果
使用USB接口