1 定义
适配器模式(Adapter Pattern)属于结构型设计模式之一,它主要用于处理不兼容的两个接口的协同工作,使一个接口转换成期望的另一个接口。例如现实场景中,手机充电器便是一个适配器,它将220V的电压转换成我们手机需要的5V电压、又例如读卡器也是一个适配器,它将SD卡转换成电脑的USB插口。在代码使用上,适配器模式其实就是增加一个适配器的类作为中间类将不兼容的接口转变成调用者需要的接口。
2 实现
适配器模式一般包含3个角色,分别是:
- 被适配者(Adaptee),就是原有已存在的需要适配兼容的类或接口,例如上述定义举例的220V电压和SD卡。
- 目标接口(Target),就是我们期望转换后的接口,目标可以是具体或抽象的类或接口,例如上述定义举例的5V电压和USB接口。
- 适配器(Adapter),作为中间桥梁将被适配者转换成目标接口。
在使用上根据被适配者和适配器的关系,适配器模式又可分类为:
- 类适配器,被适配者和适配器的关系是继承关系。
- 对象适配器,被适配者和适配器的关系是关联关系。
- 接口适配器,适配器是一个抽象类来适配被适配者的接口。
2.1 类适配器
我们继续使有手机充电器的例子来通过代码表现出适配过程。因为国家民用电是220V,而手机充电一般只需要5V,所以要想给手机充电就需要一个电压适配器将220V的电压降至5V的电压输出给手机,这也就是我们的手机充电器了。
被适配者类,它是现有的一个220V输出电的方案:
public class Power220 {
private final int mVoltage = 220;
public int output() {
System.out.println("Power220输出" + mVoltage + "V的电");
return mVoltage;
}
}
目标接口类,它期望是一个5V电的输出方案:
public interface IPower5 {
int output();
}
适配器类,它负责将电压从220V降至5V,PowerAdapter使用了类继承的方式进行了适配:
public class PowerAdapter extends Power220 implements IPower5 {
@Override
public int output() {
int voltage = super.output() / 44;
System.out.println("PowerAdapter输出" + voltage + "V的电");
return voltage;
}
}
调用者
IPower5 power5 = new PowerAdapter();
int voltage = power5.output();
System.out.println("获取" + voltage + "V的电");
输出结果:
Power220输出220V的电
PowerAdapter输出5V的电
获取5V的电
2.2 对象适配器
对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,实现上仅需要简单修改一下适配器类即可。
适配器类,它负责将电压从220V降至5V,PowerAdapter仅继承了IPower5,而通过构造方法传入Power220对象作为来进行适配:
public class PowerAdapter implements IPower5 {
private Power220 mPower220;
public PowerAdapter(Power220 power220){
mPower220 = power220;
}
@Override
public int output() {
int voltage = mPower220.output() / 44;
System.out.println("PowerAdapter输出" + voltage + "V的电");
return voltage;
}
}
调用者
IPower5 power5 = new PowerAdapter(new Power220());
int voltage = power5.output();
System.out.println("获取" + voltage + "V的电");
输出结果:
Power220输出220V的电
PowerAdapter输出5V的电
获取5V的电
2.3 接口适配器
接口适配器有别于上述类适配器和对象适配器,它是通过抽象类作为适配器类来实现复杂接口的适配。想象一下假如存在一个有多个方法需要继承实现的接口,而实际上我们又仅需要其中一个或几个必需的方法其它方法并非必要,这时如果直接实现该接口时,往往需要给所有的方法都进行实现,哪怕是方法内置空,在这情况下我们的代码就会变得臃肿而且不美观。这时如果使用接口适配器的方式进行实现的话就不会有上述的不佳表现,我们需要定义一个抽象类作为该接口的适配器,在抽象类中实现接口的所有逻辑上非必要的方法并进行置空,这样使用者便可直接继承抽象类并选择性地实现需要的方法。
被适配者接口,它需要继承类实现下方所有方法:
public interface INetworkRequest {
void start();
void progress(int progress);
void complete(int code, String result);
void success(String result);
void fail(int code);
}
适配器类,它是一个继承了被适配者接口INetworkRequest的抽象类,它实现了非必要方法,只留着必要方法给继承它的子类实现:
public abstract class NetworkRequestAdapter implements INetworkRequest {
@Override
public void start() {
}
@Override
public void progress(int progress) {
}
@Override
public void complete(int code, String result) {
}
public abstract void success(String result);
public abstract void fail(int code);
}
客户端:
public static void main(String[] args) {
// 普通直接不需要适配的写法
setListener(new INetworkRequest() {
@Override
public void start() {
// TODO...
}
@Override
public void progress(int progress) {
// TODO...
}
@Override
public void complete(int code, String result) {
// TODO...
}
@Override
public void success(String result) {
// TODO...
}
@Override
public void fail(int code) {
// TODO...
}
});
// 使有了接口适配器的写法
setListener(new NetworkRequestAdapter() {
@Override
public void success(String result) {
// TODO...
}
@Override
public void fail(int code) {
// TODO...
}
});
}
private static void setListener(INetworkRequest listener) {
// TODO...
}
3 总结
类适配器和对象适配器使用场景几乎一致,仅实现手段有不同,二者一般使用于存在一些现有的类,而此类的方法不符合期望需要从而要进行转换逻辑的适配。另外要注意的是,因为像Java等语言是不支持多类继承的,如果“目标接口(Target)”并非一个接口而是一个类的话,类适配器的实现就会受到限制。
接口适配器一般使用于要实现一个接口是多方法,而实际仅需实现少部分必要方法时,先使用抽象类来实现该接口,在抽象类中实现了非必要方法,让调用者由原来继承接口改成继承抽象类。
适配器模式的优点在于无需修改原有代码情况下复用现存的类,解决现存类和当前环境使用不兼容的问题。它也有其缺点,如果项目中过多的使用适配器,也会使代码变得复杂难懂。