什么是适配器模式
适配器模式是将一个接口转换成客户希望的另一个接口,使得一个类的接口和另一个类的接口匹配起来,无需修改原来的接口内容。
在适配器模式中,我们通过增加一个新的适配器类来解决接口兼容问题。使得原本没有任何关系的类可以协同工作。根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器。在对象适配器模式中,适配器和适配者之间是关联关系;在类适配器模式中,适配器和适配者之间是继承或实现关系。实际开发中对象适配器的适用频率更高。
对象适配器模式包含以下几个角色:
Target(目标抽象类):定义客户需要的接口,可以是抽象类或接口也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器模式中通过继承Target并关联一个Adaptee对象进行关联。
Adaptee(适配者类):适配者即被适配的对象,定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了具体的业务方法。
适配器模式的优缺点
优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原代码。
- 将具体业务实现封装在适配者类中,增加了类的复用性,对客户端是透明的。
- 一个对象适配器可以把多个不同的适配者适配到同一个目标。
- 可以适配一个适配者的子类,由于适配器和适配者是关联关系,根据里氏替换原则适配者的子类也可以通过该适配器进行适配。
类适配器模式还有一个优点,由于适配器类是适配者类的子类,因此可以在适配器类中修改一些适配者的方法,是的适配器更加灵活。
缺点
类适配器模式缺点
- 对于java,C#等不支持多继承的语言,一次最多只能适配一个适配者类。
- 对于java,C#等语言中,目标抽象类只能为接口,不能为类,使用有一定的局限性。
- 适配者类不能为final修饰。
对象适配器模式缺点
- 与类适配器相比,如果想要在适配器中修改适配者类中的方法必须替换适配者类的方法。
适配器模式的应用场景
- 系统需要使用一些现有的类,而这些类的接口不符合现有需求。
- 想新建一个可以重复使用的类,用于与其他没有关联的类一起工作。
适配器模式的案例
// 目标接口(抽象类)
public interface IVoltage5V {
int output5V();
}
// 适配者类
public class Voltage220V {
public int output220V() {
int src = 220;
System.out.println("===" + src + "===");
return src;
}
}
// 适配器类
public class VoltageAdapter implements IVoltage5V {
private final Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220v) {
this.voltage220V = voltage220v;
}
@Override
public int output5V() {
int dst = 0;
if (null != voltage220V) {
int src = voltage220V.output220V();
dst = src / 44;
System.out.println("===" + dst);
}
return dst;
}
}
public class Phone {
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("5V~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println(">5V~~");
}
}
}
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
适配器模式在源码中的应用
InputStreamReader
// 目标抽象类
public abstract class Reader implements Readable, Closeable {
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}
......
}
// 适配器类
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
/**
* Creates an InputStreamReader that uses the default charset.
*
* @param in An InputStream
*/
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
public int read() throws IOException {
return sd.read();
}
......
}
// 适配者类
public class StreamDecoder extends Reader {
public int read() throws IOException {
return this.read0();
}
private int read0() throws IOException {
synchronized(this.lock) {
if (this.haveLeftoverChar) {
this.haveLeftoverChar = false;
return this.leftoverChar;
} else {
char[] var2 = new char[2];
int var3 = this.read(var2, 0, 2);
switch (var3) {
case -1:
return -1;
case 0:
default:
assert false : var3;
return -1;
case 2:
this.leftoverChar = var2[1];
this.haveLeftoverChar = true;
case 1:
return var2[0];
}
}
}
}
......
}