适配器模式的介绍和定义
适配器模式,即定义一个包装类,用于包装不兼容接口的对象,从而使原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
适配器模式具有两种:
- 类适配器模式;
- 对象适配器模式;
适配器模式的应用场景:
- 当你希望使用某些现有的类,但其接口与您的其他代码不兼容是,请使用适配器类。
- 当你希望重用几个现有的子类,这些子类缺少一些不能添加到超类中的公共功能时,请使用该模式。
适配器模式结构和实现
适配器模式分为两种:对象适配器模式和类适配器模式。接下来分别来了解两种适配器模式的结构和实现。
类适配器模式
类适配器模式结构如下:
在类适配器中,适配器Adapter实现了声明的目标接口,同时适配器Adapter也继承了被适配对象Adaptee。接下来我们以代码示例来实现一下类适配器模式,这里以最常见的电压转换为例。
/**
* 目前接口(目标电压)
*/
interface Voltage5V{
int output5V();
}
/**
* 源输出类(原始电压)
*/
class Voltage220V{
int output220V(){
return 220;
}
}
/**
* 适配器类(电压转化)
*/
class VoltageAdapter extends Voltage220V implements Voltage5V {
@Override
public int output5V() {
int vol = output220V();
System.out.println("电压适配器开始工作,起始电压:" + vol + "V");
int convertVol = vol / 44;
System.out.println("电压适配器工作完成,转换后电压:" + convertVol + "V");
return convertVol;
}
}
客户端测试代码如下:
public class ClassAdapterTest {
public static void main(String[] args) {
VoltageAdapter voltageAdapter = new VoltageAdapter();
voltageAdapter.output5V();
}
}
运行结果如下:
电压适配器开始工作,起始电压:220V
电压适配器工作完成,转换后电压:5V
对象适配器模式
对象适配器模式的结构如下:
与类适配器模式不同的是,适配器类Adapter不再继承被适配类Adaptee,而是在类中维护一个全局变量,使用组合模式实现。针对上面的类适配器模式进行改造如下:
/**
* 目前接口(目标电压)
*/
interface Voltage5V{
int output5V();
}
/**
* 源输出类(原始电压)
*/
class Voltage220V{
int output220V(){
return 220;
}
}
class VoltageAdapter implements Voltage5V {
private Voltage220V voltage220V;
VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int vol = voltage220V.output220V();
System.out.println("电压适配器开始工作,起始电压:" + vol + "V");
int convertVol = vol / 44;
System.out.println("电压适配器工作完成,转换后电压:" + convertVol + "V");
return convertVol;
}
}
客户端测试代码如下:
public class ObjectAdapterTest {
public static void main(String[] args) {
Voltage220V v = new Voltage220V();
VoltageAdapter voltageAdapter = new VoltageAdapter(v);
voltageAdapter.output5V();
}
}
测试结果如下:
电压适配器开始工作,起始电压:220V
电压适配器工作完成,转换后电压:5V
适配器模式的优缺点
对象适配器模式
优点
- 更好的复用性。系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
- 透明、简单。客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接。
- 更好的扩展性。在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
- 解耦性。将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
- 符合开放-关闭原则。同一个适配器可以把适配者类和它的子类都适配到目标接口;可以为不同的目标接口实现不同的适配器,而不需要修改待适配类。
缺点
- 使用复杂
- 需要引入对象实例
类适配器模式
优点
- 使用方便,代码简化
- 仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例
缺点
-
高耦合,灵活性低
-
使用对象继承的方式,是静态的定义方式
使用继承的关系导致适配器只能继承一个类,同时在通过继承的方法,适配器Adapter从被适配者继承方法,导致客户端调用时方法混淆,当被适配者存在多个方法时客户端需要甄别方法。
适配器模式源码中的应用
在JDK源码中,将字节输入流转换为字符输入流,就是使用的适配器模式,这里字节输入流(InputStream)作Adaptee角色,字符输入流(InputStreamReader)则扮演者适配器Adapter的角色,而Reader接口则作为Target角色。我们首先来看一下字符输入流的整个实现结构:
在字符输入流中,StreamDecoder持有字节输入流,读取和解码的过程均通过StreamDecoder,而InputStream提供了Reader各个方法的具体实现。应该说StreamDecoder和InputStreamReader共同组成了字节转换为字符的适配器。这种适配器模式使用的是对象适配器模式,InputStreamReaeder和StreamDecoder并没有实现InputStream接口,为什么要这样设计呢?我理解的是,InputStream有很多实现,包括FileInputStream、BufferdeInputStream、PrintStream等,如果是实现InputStream接口,很明显其他字节数据流就不能通过这种模式转换了,所以这里使用的是对象适配器模式。