适配器模式用于将一个接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
Adapter is a structural design pattern that transforms an interface into another interface for the users' desires
and allows objects with incompatible interfaces to collaborate.
结构设计
适配器模式包含如下角色:
Target:目标类,描述了其他类与客户端代码合作时必须遵循的协议,简单来说,就是客户端使用的目标接口。
Adaptee:适配者类,待适配的类。客户端与其接口不兼容,因此无法直接调用其功能。
Adapter:适配器类,适配器(Adapter)是一个可以同时与客户端和服务交互的类:它在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。
Client:客户端类,客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。 因此,开发者可以向程序中添加新类型的适配器而无需修改已有代码。这在服务类的接口被更改或替换时很有用:开发者无需修改客户端代码就可以创建新的适配器类。适配器模式有对象适配器和类适配器两种实现:
(1) 对象适配器:
(2) 类适配器:
伪代码实现
接下来将使用代码介绍下适配器模式的实现。首先是对象适配器模式的实现:
// 1、目标类,对客户端需要使用的目标接口进行声明
public interface Target {void request();
}// 2、适配者类,待适配类,其声明的接口无法被客户端直接调用
public class Adaptee {public void specialRequest() {System.out.println("---------do some thing in an adaptee instance---------");}
}// 3、适配器类,适配器可以接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。
// 对象适配器通过组合适配者实例并实现目标类来完成适配
public class Adapter implements Target {private Adaptee adaptee = new Adaptee();@Overridepublic void request() {adaptee.specialRequest();}
}// 4、客户端调用
public class AdapterClient {public void test() {Target adapter = new Adapter();adapter.request();}
}
其次是类适配器模式的实现:
// 1、目标类,对客户端需要使用的目标接口进行声明
public interface Target {void request();
}// 2、适配者类,待适配类,其声明的接口无法被客户端直接调用
public class Adaptee {public void specialRequest() {System.out.println("---------do some thing in an adaptee instance---------");}
}// 3、适配器类,适配器可以接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。
// 类适配器通过继承适配者实例并实现目标类来完成适配
public class Adapter extends Adaptee implements Target {@Overridepublic void request() {specialRequest();}
}// 4、客户端调用
public class AdapterClient {public void test() {Target adapter = new Adapter();adapter.request();}
}
从上面的实现不难发现,类适配器是基于继承实现,而对象适配器是基于组合关系实现。由于对象适配器是通过关联关系进行耦合的,因此在设计时更灵活,而类适配器就只能通过重写Adaptee的方法进行扩展。
适用场景
在以下情况下可以考虑使用适配器模式:
(1) 需要使用某个现有类,但是这些类的接口不符合系统的需要,可以考虑使用适配器。如以下场景:
适配器模式允许创建一个中间层类, 其可作为代码与遗留类、第三方类或提供接口的类之间的转换器。
如果需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。
扩展每个子类,将缺少的功能添加到新的子类中,且无法将功能提取到父类。 但是, 必须在所有新子类中重复添加这些代码,这样会使得代码有坏味道。
(2) 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作,可以考虑使用适配器。
(3) 将缺失功能添加到一个适配器类中是一种优雅的解决方案。 在这方案中,开发者可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。 这种方式同装饰模式非常相似。
优缺点
适配器让接口不兼容的对象可以相互合作。适配器模式有以下优点:
(1) 单一职责。可以将接口或数据转换代码从程序主要业务逻辑中分离。
(2) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
(3) 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端来说是透明的,而且提高了适配者的复用性。
(4) 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
类适配器模式还具有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还具有如下优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
但是适配器模式也存在以下缺点:
(1) 代码整体复杂度增加, 因为开发者需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。
类适配器模式还具有如下缺点:
对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
对象适配器模式还具有如下缺点:
与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。
参考
《设计模式:可复用面向对象软件的基础》 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides 著 李英军, 马晓星 等译
https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/adapter.html 适配器模式
https://blog.csdn.net/ShuSheng0007/article/details/116161690 秒懂设计模式之适配器模式
https://www.runoob.com/design-pattern/adapter-pattern.html 适配器模式
https://www.cnblogs.com/adamjwh/p/9033549.html 简说设计模式——适配器模式
https://refactoringguru.cn/design-patterns/adapter 适配器模式