《深入理解 Java 中的适配器模式》
适配器模式
一、适配器模式的分类及主要角色
适配器模式主要分为类适配器模式、对象适配器模式和接口适配器模式。
(一)主要角色
- 目标(Target)接口:代表着当前系统业务所热切期盼的接口,可以是抽象类或接口的形式。它定义了客户端所期望的行为和方法。
- 适配者(Adaptee)类:作为被访问和适配的对象,它是现存组件库中的组件接口。通常是已经存在的、具有特定功能但接口不符合当前需求的类。
- 适配器(Adapter)类:如同一个神奇的转换器,通过继承或引用适配者的对象,将适配者接口成功转换成目标接口,使得客户能够按照目标接口的格式来访问适配者。它承担着将适配者的接口转换为目标接口的重任。
二、类适配器模式
类适配器通过定义一个适配器类,既实现当前系统的业务接口,又继承现有组件库中已有的组件来达成目标。
(一)代码示例及详细注释
// 适配者:复杂的电子设备,具有不同的电压输出
public class ComplexElectronicDevice {
// 输出多种不同的电压,这里以三种电压为例
public int outputVoltage() {
System.out.println("输出复杂的电压,可能是不同数值");
return (int) (Math.random() * 100) + 100; // 随机生成一个 100 到 200 的电压值
}
}
// 目标:特定标准的直流电压接口
public interface StandardDCVoltage {
// 输出标准的直流电压值
public int outputStandardDC();
}
// 适配器类(电源适配器)
public class PowerAdapter extends ComplexElectronicDevice implements StandardDCVoltage {
// 实现目标接口方法,将复杂电子设备的电压转换为标准直流电压
@Override
public int outputStandardDC() {
// 获取复杂电子设备输出的电压值
int complexVoltage = super.outputVoltage();
// 进行相对复杂的转换逻辑,假设这里是除以一个随机系数
int randomCoefficient = (int) (Math.random() * 10) + 5; // 生成一个 5 到 15 的随机系数
int standardDC = complexVoltage / randomCoefficient;
System.out.println(complexVoltage + "V 适配转换成" + standardDC + "V");
return standardDC;
}
}
(二)类适配器的缺点
类适配器存在一个显著的缺点,即违背了合成复用原则。以这个例子来说,如果需要适配的设备输出的电压范围发生了很大变化,比如从原本的 100 - 200V 变成了 300 - 400V,那就需要另外创建一个针对新电压范围的适配器。并且由于 Java 是单继承的特性,如果不断有新的不同电压范围的适配者出现,就不得不无限地新增适配器,这会导致代码变得臃肿和难以维护。
三、对象适配器模式
对象适配器的实现方式是将现有组件库中已经实现的组件引入适配器类中,同时该类实现当前系统的业务接口。
(一) 代码案例
// 电源接口,定义输出电压的通用方法
public interface PowerSource {
int output();
}
// 适配者:不同类型的电源设备,这里以两种为例
public class HighVoltagePowerSource implements PowerSource {
// 输出高电压,比如 250V
@Override
public int output() {
System.out.println("输出高电压电源,250V");
return 250;
}
}
public class MediumVoltagePowerSource implements PowerSource {
// 输出中等电压,比如 180V
@Override
public int output() {
System.out.println("输出中等电压电源,180V");
return 180;
}
}
// 目标:特定标准的直流电压接口
public interface StandardDCVoltage {
// 输出标准的直流电压值
public int outputStandardDC();
}
// 带参构造函数注解
@AllArgsConstructor
public class PowerAdapter implements StandardDCVoltage {
// 适配者对象
private PowerSource powerSource;
// 实现目标接口方法,将适配者的电压转换为标准直流电压
@Override
public int outputStandardDC() {
// 获取适配者输出的电压值
int inputVoltage = powerSource.output();
// 进行更复杂的转换逻辑,根据输入电压动态调整转换系数
int conversionCoefficient;
if (inputVoltage > 200) {
conversionCoefficient = (int) (Math.random() * 8) + 10; // 对于高电压,生成一个 10 到 18 的随机系数
} else {
conversionCoefficient = (int) (Math.random() * 6) + 8; // 对于中等电压,生成一个 8 到 14 的随机系数
}
int standardDC = inputVoltage / conversionCoefficient;
System.out.println(inputVoltage + "V 适配转换成" + standardDC + "V");
return standardDC;
}
}
(二)对象适配器的优势
从上述代码可以看出,此模式只实现了目标接口,没有继承适配者。而是让适配者类实现适配者接口,并在适配器中引入适配者接口。当需要使用不同的适配者通过适配器进行转换时,无需再新建适配器类。例如,若需要将不同电压的电源转换成标准的直流电压,客户端只需在调用适配器时传入相应电压的电源类即可,无需新建针对特定电压的适配器。这大大提高了代码的灵活性和可扩展性。
四、接口适配器模式
接口适配器主要用于解决类臃肿的问题。当存在多个相近的适配需求时,可以把所有相关方法都整合到同一个接口中,实现所有方法后,客户端根据需要直接调用相应方法即可。
(一) 代码案例
// 这里例子:输出不同标准的直流电接口
public interface MultipleDCVoltage {
// 输出较低标准的直流电压,比如 5V
int outputLowDC();
// 输出中等标准的直流电压,比如 12V
int outputMediumDC();
// 输出较高标准的直流电压,比如 24V
int outputHighDC();
// 输出更高标准的直流电压,比如 30V
int outputExtraHighDC();
}
// 带参构造函数注解
@AllArgsConstructor
public class PowerAdapter implements MultipleDCVoltage {
// 适配者对象
private PowerSource powerSource;
// 实现输出较低标准直流电压方法
@Override
public int outputLowDC() {
// 根据适配者的电压进行复杂转换得到 5V
int inputVoltage = powerSource.output();
int conversionCoefficient = calculateCoefficientForLowDC(inputVoltage);
return inputVoltage / conversionCoefficient;
}
// 实现输出中等标准直流电压方法
@Override
public int outputMediumDC() {
// 根据适配者的电压进行复杂转换得到 12V
int inputVoltage = powerSource.output();
int conversionCoefficient = calculateCoefficientForMediumDC(inputVoltage);
return inputVoltage / conversionCoefficient;
}
// 实现输出较高标准直流电压方法
@Override
public int outputHighDC() {
// 根据适配者的电压进行复杂转换得到 24V
int inputVoltage = powerSource.output();
int conversionCoefficient = calculateCoefficientForHighDC(inputVoltage);
return inputVoltage / conversionCoefficient;
}
// 实现输出更高标准直流电压方法
@Override
public int outputExtraHighDC() {
// 根据适配者的电压进行复杂转换得到 30V
int inputVoltage = powerSource.output();
int conversionCoefficient = calculateCoefficientForExtraHighDC(inputVoltage);
return inputVoltage / conversionCoefficient;
}
// 辅助方法,根据输入电压计算输出 5V 所需的转换系数
private int calculateCoefficientForLowDC(int inputVoltage) {
// 复杂的计算逻辑,根据输入电压动态调整
if (inputVoltage > 200) {
return (int) (Math.random() * 40) + 40; // 对于高电压,生成一个 40 到 80 的随机系数
} else {
return (int) (Math.random() * 30) + 30; // 对于中等及以下电压,生成一个 30 到 60 的随机系数
}
}
// 辅助方法,根据输入电压计算输出 12V 所需的转换系数
private int calculateCoefficientForMediumDC(int inputVoltage) {
// 复杂的计算逻辑,根据输入电压动态调整
if (inputVoltage > 200) {
return (int) (Math.random() * 20) + 15; // 对于高电压,生成一个 15 到 35 的随机系数
} else {
return (int) (Math.random() * 15) + 12; // 对于中等及以下电压,生成一个 12 到 27 的随机系数
}
}
// 辅助方法,根据输入电压计算输出 24V 所需的转换系数
private int calculateCoefficientForHighDC(int inputVoltage) {
// 复杂的计算逻辑,根据输入电压动态调整
if (inputVoltage > 200) {
return (int) (Math.random() * 10) + 8; // 对于高电压,生成一个 8 到 18 的随机系数
} else {
return (int) (Math.random() * 8) + 6; // 对于中等及以下电压,生成一个 6 到 14 的随机系数
}
}
// 辅助方法,根据输入电压计算输出 30V 所需的转换系数
private int calculateCoefficientForExtraHighDC(int inputVoltage) {
// 复杂的计算逻辑,根据输入电压动态调整
if (inputVoltage > 200) {
return (int) (Math.random() * 7) + 6; // 对于高电压,生成一个 6 到 13 的随机系数
} else {
return (int) (Math.random() * 6) + 5; // 对于中等及以下电压,生成一个 5 到 11 的随机系数
}
}
}
五、适配器模式的适用场景、优点和缺点
(一)适用场景
- 当已经存在的类的方法与需求不匹配(方法结构相同或相似)时。比如一个旧的系统中的类的接口与新的业务需求不兼容,通过适配器可以使旧类在新系统中继续发挥作用。
- 使用第三方提供的组件,但组件接口定义与自身要求的接口定义不同。在集成第三方库时,常常会遇到接口不一致的情况,适配器模式可以解决这个问题。
(二)优点
- 能提高类的透明性和复用性,现有的类可以复用而无需改变。通过适配器,可以在不修改原有类的情况下,使其适应新的接口需求,提高了代码的可复用性。
- 目标类和适配器类解耦,提升程序的扩展性。目标类只关心自己的接口,而适配器类负责将适配者的接口转换为目标接口,两者之间的解耦使得程序更容易扩展和维护。
- 在很多业务场景中符合开闭原则。当需要适配新的类时,可以通过创建新的适配器类来实现,而不需要修改原有代码。
(三)缺点
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性。由于适配器需要同时考虑目标接口和适配者接口,以及两者之间的转换逻辑,所以编写适配器可能会比较复杂。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。如果在一个系统中大量使用适配器,可能会导致代码结构变得复杂,难以理解和维护。