适配器模式(Adapter Pattern),又称作变压器模式(因为这两者都体现了“转换”或“适配”的核心概念),是一种结构型设计模式。它将一个类的接口转换成客户端所期望的另一种接口,从而使得原本因接口不匹配而无法协同工作的两个类能够一起工作。
一、工作原理
适配器模式的工作原理是通过创建一个适配器类,该类包含一个源接口和一个目标接口。适配器类将客户端请求转换为源接口可以理解的命令,并执行相应的操作。这样,客户端只需要与适配器交互,而不需要直接与源接口交互,从而实现了接口的转换和适配。
二、主要角色
- 目标接口(Target):定义客户端使用的接口,也就是客户端期望的接口。
- 适配器(Adapter):实现目标接口,并持有一个源接口的引用,用于将客户端的请求转换成对源接口的调用。
- 源接口(Adaptee):需要被适配的接口,即系统中已经存在的、但接口与目标接口不兼容的类。
- 客户端(Client):使用目标接口来调用适配器的方法,从而间接调用源接口的方法。
三、实现方式
适配器模式有两种主要的实现方式:对象适配器和类适配器。
-
对象适配器:
- 符合组合复用原则,使用了委托机制。
- 在适配器类中维护一个被适配者(源接口)的成员变量,通过该成员变量调用被适配者的方法。
-
类适配器:
- 通过类的继承关系实现适配器模式。
- 适配器类继承被适配者类,通过super关键字调用被适配者的方法。
(注:在实际开发中,推荐使用对象适配器模式,因为组合通常比继承更加灵活,且更符合开闭原则。)
四、适用场景
- 功能正确但接口不匹配:对于之前开发好的类,其操作和返回值都是正确的,但其定义的方法接口无法调用。此时可以使用适配器模式,使该类与用户的接口匹配,让用户使用适配器的接口,间接调用该类。
- 软件维护阶段:在软件维护时,出现操作和返回值类似但函数接口不同的情况,为了适配第三方系统的接口,可以使用适配器模式。
- 多个类的接口统一:当系统中的多个类的接口不统一时,可以使用适配器模式将它们的接口统一成一个接口,使这些类能够协同工作,提高系统的灵活性和可扩展性。
五、优缺点
-
优点:
- 提高代码的复用性和灵活性:通过适配器模式,可以让原本不兼容的接口协同工作。
- 降低耦合:目标类和现有类(被适配者)解除耦合,降低了系统的耦合性,易于扩展和维护。
- 符合开闭原则:如果需要修改或扩展功能,只需要修改适配器类即可,目标类和现有类各自独立,互不影响。
-
缺点:
- 增加代码的复杂度和维护成本:适配器模式需要增加一个额外的适配器类,增加了代码的量。如果设计不当,可能会导致适配器类的滥用,增加代码的混乱程度。
- 降低可读性:系统代码可读性可能会降低,因为调用系统接口时,如果调用的是适配器接口,还需要查找调用的是哪个现有类的实际接口。
六、代码示例
示例一:电压适配器
假设我们有一个输出220V电压的设备(Voltage220V
类),但我们有一个只能接受5V电压的设备(如手机,Phone
类)。此时,我们可以使用适配器模式来创建一个电压适配器(VoltageAdapter
类)。
// 被适配类:表示输出220V电压的设备
public class Voltage220V {public int output220V() {int src = 220;System.out.println("电压=" + src + "伏");return src;}
}// 适配接口:表示需要适配到的5V电压接口
public interface IVoltage5V {int output5V();
}// 适配器类:将220V电压适配为5V电压
public class VoltageAdapter implements IVoltage5V {private Voltage220V voltage220V;public VoltageAdapter(Voltage220V voltage220V) {this.voltage220V = voltage220V;}@Overridepublic int output5V() {int srcV = voltage220V.output220V(); // 获取220V电压int dstV = srcV / 44; // 转换为5V电压(这里仅为示例,实际转换可能更复杂)return dstV;}
}// 客户端类:表示只能接受5V电压的设备,如手机
public class Phone {// 充电方法,接受5V电压接口public void charging(IVoltage5V iVoltage5V) {if (iVoltage5V.output5V() == 5) {System.out.println("电压为5v,可以充电");} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于5v,无法充电");}}
}// 客户端测试代码
public class Client {public static void main(String[] args) {Phone phone = new Phone();Voltage220V voltage220V = new Voltage220V();VoltageAdapter voltageAdapter = new VoltageAdapter(voltage220V);phone.charging(voltageAdapter); // 通过适配器为手机充电}
}
在这个示例中,VoltageAdapter
类作为适配器,将Voltage220V
类的220V电压输出转换为5V电压输出,从而满足了Phone
类的充电需求。
示例二:三相插座与两相插座的适配器
假设我们有一个三相插座(ThreeElectricOutlet
接口)和一个两相插座(TwoElectricOutlet
接口),以及一个只能插入两相插座的电视机(Tv
类)。此时,我们可以使用适配器模式来创建一个三相到两相的插座适配器(TreeElecricAdapter
类)。
// 三相插座接口
public interface ThreeElectricOutlet {void connectElectricCurrent();
}// 两相插座接口
public interface TwoElectricOutlet {void connectElectricCurrent();
}// 电视机类,实现了两相插座接口
public class Tv implements TwoElectricOutlet {private String name;public Tv() {name = "电视机";}public Tv(String name) {this.name = name;}@Overridepublic void connectElectricCurrent() {System.out.println(name + "开始播放节目");}
}// 三相到两相的插座适配器
public class TreeElecricAdapter implements ThreeElectricOutlet {TwoElectricOutlet outlet;public TreeElecricAdapter(TwoElectricOutlet teo) {this.outlet = teo;}@Overridepublic void connectElectricCurrent() {outlet.connectElectricCurrent();}
}// 客户端测试代码
public class TestActivity {public static void main(String[] args) {ThreeElectricOutlet outlet;Tv tv = new Tv("长虹电视机");TreeElecricAdapter adapter = new TreeElecricAdapter(tv);outlet = adapter;outlet.connectElectricCurrent(); // 通过适配器为电视机供电}
}
题外:适配器模式为什么又被称为变压器模式
适配器模式(Adapter Pattern)之所以又被称为变压器模式,是因为这两者都体现了“转换”或“适配”的核心概念。以下是对这一命名由来的详细解释:
1、功能上的相似性
-
适配器模式:
- 主要解决的是接口不匹配的问题。
- 通过适配器类,将一个类的接口转换成客户端所期望的另一种接口,从而使得原本因接口不匹配而不能一起工作的两个类能够一起工作。
-
变压器:
- 在电力系统中,变压器的主要功能是将一种电压等级的电能转换成另一种电压等级的电能。
- 通过变换电压,使得不同电压等级的电力系统能够相互连接和传输电能。
从功能上来看,适配器模式和变压器都起到了“转换”或“适配”的作用,使得原本不兼容的双方能够协同工作。
2、命名上的直观性
-
适配器:
- 这个名称直接描述了该模式的主要功能,即适配或转换接口。
- 在计算机领域中,适配器通常用于连接不同规格或类型的设备,使它们能够相互通信或协同工作。
-
变压器:
- 这个名称同样直观地描述了其转换电压的功能。
- 在电力系统中,变压器是不可或缺的转换设备。
将适配器模式命名为“变压器模式”,可以直观地反映出该模式在接口转换方面的作用,使得理解和记忆都更加方便。
综上所述,适配器模式是一种非常有用的设计模式,它能够在不修改现有类的基础上,实现接口的转换和适配,从而提高代码的复用性和灵活性。然而,在使用适配器模式时,也需要注意其可能带来的复杂度和维护成本问题。