一文学会如何使用适配器模式
适配器模式又叫做变压器模式,它的功能是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配导致无法在一起工作的两个类能一起工作,属于结构型设计模式。
也就是说当前系统存在两种接口A和B,客户只支持访问A接口,但是当前系统没有A接口对象,但是有B接口对象,但客户无法识别B接口,因此需要通过一个适配器C,将B接口内容转换成A接口,从而使得客户能够从A接口获取到B接口内容。
在软件开发中,基本上任何问题都可以通过增加一个中间层进行解决。适配器模式其实就是一个中间层。综上,适配器模式其实起着转化/委托的作用,将一种接口转化为另一种符合需求的接口。
应用场景
提供一个转换器,将当前系统存在的一个对象转化为客户端能够访问的接口对象。适配器适用于以下几种业务场景:
- 已经存在的类,它的方法和需求不匹配的情况
- 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不同情况下的解决方案。
适配器模式一般包含三种角色:
- 目标角色(Target):期望的接口
- 源角色(Adaptee):存在于系统中,内容满足客户需求(需转换),但接口不匹配的实例
- 适配器(Adapter):将原角色转换为目标角色的实例
适配器模式各角色之间的关系如:
假设当前系统中,客户端需要访问的是Target接口,但Target接口没有一个实例符合需求,而源角色实例符合需求;但是客户端无法直接使用源角色;因此我们需要一个适配器来进行中转,让源角色转换为目标角色的接口形式。
适配器模式有三种形式:类适配器、对象适配器、接口适配器。
类适配器
类适配器的原理就是通过继承来实现适配器功能。
具体做法:让Adapter实现Target接口,并且继承Adaptee,这样Adapter就具备两者的特性,就可以将两者进行转化。下面来看类图:
在我们的日常生活中,民用电基本都是220V,我们使用电器时通常都需要电源适配器进行转换。下面我们就来模拟这个场景:
首先创建Adaptee角色,表示需要被转换的对象220V电流:
public class Ac220 {
public int outPut220V() {
int output = 220;
System.out.println("电压220V");
return output;
}
}
创建Target角色,表示5v直流电:
public interface Dc5 {
int outputDc5();
}
创建Adapter角色适配器代表电源转换头:
public class PowerAdapter extends Ac220 implements Dc5{
@Override
public int outputDc5() {
int adapterInput = super.outPut220V();
int adapterOutput = adapterInput/44;
System.out.println("使用适配器 输入:" + adapterInput+"V,输出:"+adapterOutput);
return 0;
}
}
测试代码:
public class Main {
public static void main(String[] args) {
Dc5 adapter = new PowerAdapter();
adapter.outputDc5();
}
}
上面这个代码就是我们通过电源转换头将220V转换成了5V。
对象适配器
对象适配器的原理就是通过组合来实现适配器功能。具体做法就是:让Adapter实现Target接口,然后内部持有Adaptee实例,然后再Target接口规定的方法内进行转换。
这里我们只需要修改适配器实现即可,其他和类适配器一致:
public class PowerAdapter implements Dc5 {
private Ac220 ac220;
public PowerAdapter(Ac220 ac220) {
this.ac220 = ac220;
}
@Override
public int outputDc5() {
int adapterInput = ac220.outPut220V();
int adapterOutput = adapterInput/44;
System.out.println("使用适配器 输入:" + adapterInput+"V,输出:"+adapterOutput);
return adapterInput;
}
}
接口适配器
接口适配器的关注点于类适配器和对象适配器的关注点不太一样,类适配器和对象适配器着重于将系统存在的一个角色转换成目标接口所需内容,而接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,导致类显得很臃肿。此时,使用接口适配器就能让我们只实现我们需要的接口方法,目标更清晰。
接口适配器的主要原理就是抽象类实现接口,并且空实现接口众多方法。
首先创建Target角色:
public interface Dc {
int outputV5();
int outputV12();
int outputV15();
}
创建Adaptee
角色220V电压类:
public class Ac220 {
public int outPut220V() {
int output = 220;
System.out.println("电压220V");
return output;
}
}
创建Adapter
适配器:这里实现接口,方法为空方法即可。
public class PowerAdapter implements Dc {
private Ac220 ac220;
public PowerAdapter(Ac220 ac220) {
this.ac220 = ac220;
}
@Override
public int outputV5() {
return 0;
}
@Override
public int outputV12() {
return 0;
}
@Override
public int outputV15() {
return 0;
}
}
客户端代码:我们可以在适配器里面直接编写相关实现,也可以创建实例的时候编写我们需要的方法,这样就不需要实现那么多空实现的接口方法了。
public class Main {
public static void main(String[] args) {
Dc adapter = new PowerAdapter(new Ac220()) {
@Override
public int outputV5() {
int a = new Ac220().outPut220V();
return a/5;
}
};
int result = adapter.outputV5();
System.out.println(result);
}
}
优缺点
优点
- 能提高类的透明性和复用性,现有的类复用但不需要改变。
- 目标类和适配器类解耦,提高程序的拓展性。
- 在很多场景中符合开闭原则
缺点
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。