demo描述:要给手机充电,手机只能用5v的电压充电,提供的电影是220v的,为了给手机充上电,这时候需要一个适配器,让适配器转换电压,从而使手机能正常充电。
适配器模式解决接口间的不兼容性,让原本不匹配的类可以协同工作,共3中实现方式:类适配器、对象适配器、接口适配器。
demo代码:
一、类适配器:
目标(target),手机类:
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("电压为5V,可以充电");
} else if (voltage5V.output5V() > 5) {
System.out.println("电压大于5V,要爆炸");
}
}
}
被适配者(source),现有电压:
public class Voltage220V {
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
电压接口、
适配器(adapter):
public interface Voltage5V {
int output5V();
}
public class VoltageAdapter extends Voltage220V implements Voltage5V {
@Override
public int output5V() {
//获取220V的电压
int srcV = output220V();
//转换
int dstV = srcV / 44;
return dstV;
}
}
客户端:
public class Client {
public static void main(String[] args) {
System.out.println("类适配器模式");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
demo类图:
类图分析:手机调用5v电压的接口充电,适配器实现5v电压的接口让手机能充电,实现的方法是适配器继承220v电压类,通过继承机制让适配器能使用到父类220v电压输出的方法,只要能使用父类方法后就能对父类的220v电压进行降压转换,从而将转换后的5v电压提供给手机让其正常充电
类适配器总结:类适配器通过类继承的方式让适配器可以用被适配者的方法进行适配,而目标(手机)是通过接口的方式来调用适配后的方法的,由于java是单继承机制,这就要求适配器必须去继承被适配者,这样的好处是可以根据要求重写被适配者的方法,使适配器变灵活;坏处是会使被适配者的方法在适配器中暴露出来,从而增加使用成本。
二、对象适配器:
手机类还是那个手机类、现有电压还是那个现有电压、电压接口还是那个电压接口,适配器变成了这样:
public class VoltageAdapter implements Voltage5V {
private Voltage220V voltage220V;
//用构造器而不是直接new可以增加适配器的扩展性
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int dst = 0;
if (null != voltage220V) {
//通过聚合的方式调用src方法,而不是用继承方式直接调
int src = voltage220V.output220V();
dst = src / 44;
}
return dst;
}
}
客户端:
public class Client {
public static void main(String[] args) {
System.out.println("对象适配器模式");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
demo类图:
类图分析:对象适配器不再去继承现有电压,而用用聚合现有电压类,将现有电压的对象作为适配器的属性,通过聚合方式来可以调用现有电压的方法进行电压的降压适配,在这把少用继承多用组合、聚合的原则体现的明白儿的。观察类图,如果让适配器可以适配更多种不同电压的话可以做如下更改:将1位置不同的电压抽象出一个父类、2位置适配器不再聚合具体的1,而是和1的父类产生聚合关系,同时2的构造器中参数将具体的1换成1的父类。
对象适配器总结:对象适配器比较容易理解与扩展,体现出来合成复用原则。
三、接口适配器:
接口中放可能要被适配的方法:
public interface Voltage {
int voltage5V();
int voltage10V();
int voltage20V();
}
抽象适配器空实现或默认实现接口:
public abstract class AbsAdapter implements Voltage {
@Override
public int voltage5V() {return -1;}
@Override
public int voltage10V() {return -1;}
@Override
public int voltage20V() {return -1;}
}
客户端根据实际可以重写适配器的方法进行适配:
public class Phone {
public static void main(String[] args) {
//不需要适配的时候可以调用默认的实现方法(这里写成了空实现)
AbsAdapter absAdapter1 = new AbsAdapter() {
};
absAdapter1.voltage5V();
//-----------------上边代码时为了方便对比--------------------------
//需要适配的时候只重写需要适配的方法并调用
AbsAdapter absAdapter = new AbsAdapter() {
//覆盖需要使用的接口方法
@Override
public int voltage5V() {
System.out.println("5v,放心充电");
return 5;
}
};
//target只使用m1方法
absAdapter.voltage5V();
}
}
demo类图:
类图分析:接口适配器比较抽象,电压接口功能强大了,不仅可以为手机提供合适的电压,也可以为其他电器提供合适的电压;适配器空实现或默认实现电压接口,手机/其他电器根据自己需要的电压去重写适配器中的方法,用不到的方法不用调用。这里适配器不只起适配的作用,也相当于做了一层隔离:实现机制要求实现类必须实现接口的方法,如果手机直接实现接口并重写方法的话会实现自己不需要的方法,让本来变臃肿,中间加一层实现类,让手机去调用实现类(适配器),用哪个方法调哪个方法,这样既能取自己所需又能避免无用的实现。
接口适配器总结:跳出适配器模式的视角,单看类图,VoltageAdapter就是在接口与调用者之间做了一缓冲、一层隔离,这种理念在实际开发中用的还是挺多的。