适配器介绍与讲解
- 一、概念
- 二、适配器模式结构
- 适配器分类
- 核心思想
- 核心角色
- 模式的UML类图
- 应用场景
- 模式优点
- 模式缺点
- 实例演示
- 图示
- 代码演示
- 运行结果
一、概念
适配器模式(别名:包装器)
是一种结构型设计模式
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
二、适配器模式结构
适配器分类
1、类适配器模式(Class Adapter Pattern)
- 定义;定义:类适配器模式通过继承被适配者(Adaptee)的类来实现适配器,并同时实现目标接口(Target)。由于Java不支持多继承,所以类适配器通常要求目标接口是一个接口,而不是抽象类。
- 特点:由于采用继承模式,在适配器中可以重写被适配者原有的方法,使得适配器可以更加灵活。但是,由于Java是单继承模式,适配器类只能继承被适配者类,不能再额外继承其他类,这可能导致一定的局限性。
2、对象适配器模式(Object Adapter Pattern)
- 定义:对象适配器模式通过持有被适配者的实例,并在适配器中实现目标接口,将客户端的调用请求转换为对被适配者的调用。
- 特点:对象适配器模式规避了单继承的劣势,将被适配者类用引用的方式传递给适配器,这样可以传递的是被适配者对象本身及其子类对象。这种模式更加开放,但同时也需要自己重新定义被适配者接口,可能增加额外的操作。
3、接口适配器模式(Default Adapter Pattern)
- 定义:接口适配器模式也称为默认适配器模式或空对象模式。当接口中的方法很多,而客户端只需要关心其中的一部分方法时,可以使用接口适配器模式来简化客户端的实现。
- 特点:接口适配器模式允许客户端只关心自己需要的方法,而无需实现接口中的所有方法,从而简化了客户端的实现。在JDK类库的事件处理包java.awt.event中,如WindowAdapter、KeyAdapter、MouseAdapter等都使用了接口适配器模式
核心思想
把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
- 例子:用电器来打个比喻:有一个电器的插头是三脚的,而现有的插座是两孔的,要使插头插上插座,我们需要一个插头转换器,这个转换器即是适配器。
核心角色
- 目标接口(Target):定义客户端使用的目标接口,客户端通过调用这个接口中的方法来访问适配器的功能。
- 适配器(Adapter):实现目标接口,并在内部持有一个被适配者的实例。适配器将客户端的请求委派给被适配者来完成具体的功能。
- 被适配者(Adaptee):定义了一个已存在的接口,但与目标接口不兼容,需要被适配器进行转换。
模式的UML类图
应用场景
1.旧系统接口升级:当系统升级后,新的接口可能与旧的接口不兼容。此时,可以使用适配器模式来包装旧系统的类,使其符合新系统的接口要求,从而无需修改旧系统的代码。
2.第三方库集成:当使用第三方库时,可能会发现库的接口并不符合项目的要求。通过使用适配器模式,可以创建适配器类来适配第三方库的接口,使其更容易集成到项目中。
3.多种数据库操作:在项目中可能需要支持多种数据库,每种数据库的操作接口可能不同。通过适配器模式,可以创建多个适配器类来适配不同的数据库接口,从而提供统一的数据库操作接口。
4.插件系统:在构建插件系统时,不同的插件可能具有不同的接口。通过适配器模式,可以创建适配器类来统一插件的接口,使得主程序可以更方便地与插件进行交互。
5.硬件接口适配:在软件与硬件通信的场景中,硬件提供的接口可能与软件需要的接口不匹配。通过适配器模式,可以编写软件适配器来适配硬件的接口,从而简化软件的开发
模式优点
- 安全可靠:封装了旧接口,对客户端透明,客户端代码无需修改。
- 提高复用性:可以复用不兼容的类;可以对不同的类无需修改,就可以进行组合。
- 扩展性好:适配器模式满足“开-闭原则”。当添加一个实现Adaptee接口的新类时,不必修改Adapter,Adapter就能对这个新类的实例进行适配。
- 目标(Target)和被适配者(Adaptee)是完全解耦的关系。
模式缺点
- 过多的适配器会导致系统结构复杂。
- 如果适配器没有实现好,可能会拖慢整个系统的性能。
- 滥用适配器模式会导致系统设计紊乱。
- 适配器模式需要增加一个额外的适配器类,增加了代码的量
实例演示
图示
代码演示
package task1;public interface DBSocketInterface {public void powerWithTwoRound(); }package task1;public interface GBSocketInterface {public void powerWithThreeFlat();}package task1;public class DBSocket implements DBSocketInterface {@Overridepublic void powerWithTwoRound() {// TODO Auto-generated method stubSystem.out.println("使用两项圆头的插孔供电");}}package task1;public class SocketAdapter implements DBSocketInterface {private GBSocketInterface gbSocket;public SocketAdapter(GBSocketInterface gbSocket) {// TODO Auto-generated constructor stubthis.gbSocket = gbSocket;}@Overridepublic void powerWithTwoRound() {// TODO Auto-generated method stubgbSocket.powerWithThreeFlat();}}package task1;public class GBSocket implements GBSocketInterface {@Overridepublic void powerWithThreeFlat() {// TODO Auto-generated method stubSystem.out.println("正在使用三项扁头插头供电");}}package task1;public class Hotel {private DBSocketInterface dbSocket;public void setSocket(DBSocketInterface dbSocket) {this.dbSocket = dbSocket;}public void charge() {dbSocket.powerWithTwoRound();}}
测试类:
package task1;public class Test {public static void main(String[] args) {GBSocketInterface gbSocket = new GBSocket();Hotel hotel = new Hotel();SocketAdapter socketAdapter = new SocketAdapter(gbSocket);hotel.setSocket(socketAdapter);hotel.charge();} }
运行结果
在上面的代码中, GBSocket
是目标接口的具体实现类, SocketAdapter
是适配器接口的具体实现类,并且持有一个目标接口的引用。在客户端中,我们创建了一个目标接口的实例和一个适配器接口的实例,然后通过适配器接口调用目标接口的方法。