1、适配器模式介绍
适配器模式:是一种结构型设计模式。适配器模式的思想是:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
适配器模式涉及3个角色:
- 目标角色(target):这是客户期待的接口。目标可以是具体的或抽象的类,也可以是接口.
- 适配者角色(adaptee):已有接口,但是和客户期待的接口不兼容。
- 适配器角色(adapter):将已有接口转换成目标接口,协调adaptee和target,使两者能够协同工作。
适用场景
1,系统需要使用现有的类,但现有的类却不兼容。
2,需要建立一个可以重复使用的类,用于一些彼此关系不大的类,并易于扩展,以便于面对将来会出现的类。
3,需要一个统一的输出接口,但是输入类型却不可预知。
Demo
简单的抽象一个场景:手机充电需要将220V的交流电转化为手机锂电池需要的5V直流电,我们的demo就是写一个电源适配器,将 AC220v ——> DC5V,其实适配器模式可以简单的分为三类:类适配器模式、对象的适配器模式、接口的适配器模式。我们就以这三种模式来实现上述步骤。
2、适配器模式的3中形式
适配器模式包括3种形式:类适配器模式、对象适配器模式、接口适配器模式(或又称作缺省适配器模式)。
2.1、类适配器模式
目标类Destination,只需要定义方法,由适配器来转化:
package com.lys.javatest.shipei;
public interface Target {
//将220V转换输出5V(原有插头(Adaptee)没有的)
public int convert_5v();
}
Source类
package com.lys.javatest.shipei;
public class PowerPort220V {
//原有插头只能输出220V
public int output_220v() {
System.out.println("使用电压:220V");
return 220;
}
}
创建适配器类(Adapter)
package com.lys.javatest.shipei;
public class PowerAdapter1 extends PowerPort220V implements Target {
@Override
public int convert_5v() {
int output = this.output_220v();
System.out.println("使用电压:"+ output +"V");
return output/44;
}
}
对于使用,也很简单:
package com.lys.javatest.shipei;
//通过Adapter类从而调用所需要的方法
public class AdapterPattern {
public static void main(String[] args) {
Target mAdapter220V = new PowerAdapter1();
mAdapter220V.convert_5v();
}
}
因为java单继承的缘故,Destination类必须是接口,以便于Adapter去继承Source并实现Destination,完成适配的功能,但这样就导致了Adapter里暴露了Source类的方法,使用起来的成本就增加了。
2.2、对象适配器模式
对于同样的逻辑,我们在以对象适配器模式实现。我们保留PowerPort220V 和Target两个基本类,我们让Adapter持有Destination类的实例,然后再实现Target,以这种持有对象的方式来实现适配器功能:
package com.lys.javatest.shipei;
public class PowerAdapter2 implements Target {
private PowerPort220V powerPort220V;
public PowerAdapter2(PowerPort220V powerPort220V) {
this.powerPort220V = powerPort220V;
}
@Override
public int convert_5v() {
int output = 0;
if (powerPort220V != null) {
output = powerPort220V.output_220v() / 44;
}
System.out.println("使用电压:"+output+"V");
return output;
}
}
对于使用,也很简单:
public static void initObjAdapter() {
PowerAdapter2 adapter = new PowerAdapter2(new PowerPort220V());
adapter.convert_5v();
}
public static void main(String[] args) {
initObjAdapter();
}
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。再回想装饰者模式,装饰者是对Source的装饰,使用者毫无察觉到Source被装饰,也就是用法不变。而对于适配器模式用法还是有改变的。
2.3、接口适配器模式
对于接口适配器模式,我们就不用单着眼于220->5,我们的接口可以有更多的抽象方法,我们只需要关心我们需要的回调方法,我们把接口比作万能适配器:
package com.lys.javatest.shipei.ter1;
public interface Target {
int output5V();
int output9V();
int output12V();
int output24V();
}
我们要用的是5V的电压,但是我们必须存在一个中间适配器,用于实现默认的接口方法,以至于减少我们适配器的代码量,让代码更加清晰:
package com.lys.javatest.shipei.ter1;
import com.lys.javatest.shipei.PowerPort220V;
// 这里抽象类其实就写了空方法,等着子类去实现需要的方法。
public abstract class PowerAdapter3 implements Target {
protected PowerPort220V mAC220;
public PowerAdapter3(PowerPort220V ac220) {
this.mAC220 = ac220;
}
@Override
public int output5V() {
return mAC220.output_220v();
}
@Override
public int output9V() {
return mAC220.output_220v();
}
@Override
public int output12V() {
return mAC220.output_220v();
}
@Override
public int output24V() {
return mAC220.output_220v();
}
}
我们关心5V的适配:
package com.lys.javatest.shipei.ter1;
import com.lys.javatest.shipei.PowerPort220V;
public class Power5VAdapter extends PowerAdapter3 {
public Power5VAdapter(PowerPort220V ac220) {
super(ac220);
}
@Override
public int output5V() {
int output = 0;
if (mAC220 != null) {
output = mAC220.output_220v() / 44;
}
return output;
}
}
这样一来我们就只需要重写父类我们关心的方法了,当然我们有时候可以省略Power5VAdapter类,因为内部类可以实现我们的方法,我们来看使用:
package com.lys.javatest.shipei.ter1;
import com.lys.javatest.shipei.PowerPort220V;
public class Snippet {
// 接口适配器模式demo
private static void initinterfaceAdapter() {
// 已经实现了子类
Power5VAdapter power5VAdapter = new Power5VAdapter(new PowerPort220V());
int output5v = power5VAdapter.output5V();
System.out.println(output5v);
// 直接实现子类
PowerAdapter3 powerAdapter = new PowerAdapter3(new PowerPort220V()) {
@Override
public int output5V() {
int output = 0;
if (mAC220 != null) {
output = mAC220.output_220v() / 44;
}
return output;
}
};
int output5v2 = powerAdapter.output5V();
System.out.println(output5v2);
}
public static void main(String[] args) {
initinterfaceAdapter();
}
}
3、适配器模式总结
适配器模式优点
- a、复用性:系统需要使用已经存在的类,功能符合系统要求,但这个类的接口不符合系统的需求,通过适配器模式解决不兼容的问题,使这些功能类得到复用。
- b、扩展性:适配器使得系统多了一个方式扩展系统的功能;
- c、耦合性:一定程度上的解耦.
适配器模式缺点:
- 过多地使用适配器,增加系统理解难度。
4、适配器模式应用场景
类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景:
(1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
(2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。
接口适配器使用场景:
(1)想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。