连接多元世界的纽带------适配器模式的魅力

📚 A公司正在开发一个电子商务平台,团队决定集成一个新的服务。例如支付宝、微信等第三方的支付选项给用户,然而,我们的服务之前的支付接口和它们的接口是不兼容的,导致无法直接继承,需要修改之前的代码,比较麻烦,我们想在不改变现有支付系统的前提下,将支付宝的支付系统集成在我们的平台中。
🙋♂️
适配器模式
就可以解决以上问题,通过创建一个适配器,我们可以使不兼容的系统相互调用。在本篇文章中,我们就开始学习适配器模式吧~
1. 概述
适配器模式
是通过引入一个叫做适配器 的包装类,将一个接口转换为客户希望的另一个接口(适配者),使这两个不兼容的接口可以在一个系统中工作。
🏷️ 适配器模式有两种表现形式,分别为类适配器模式 和对象适配器模式。
- 对象适配器: 适配器和适配者之间是关联关系;
- 类适配器: 适配器和适配者之间是继承或者实现关系;
❗ 简单理解适配器模式Java中的
Arrays
有个排序(sort)
方法,只能对数组进行排序,但是如果我们想要对List
排序,就需要引入一个适配器,对List
做处理,调用Arrays
中的排序方法实现这个功能。你懂了吗?😉
2. 结构
适配器模式由以下角色组成:
- 目标抽象类(Target):
目标抽象类
就是客户端直接调用的接口,可以是一个抽象类或接口,也可以是一个具体的类; - 适配器类(Adapter):
适配器类
可以调用另一个接口,对目标抽象类
和适配者类
适配,使二者产生联系,是适配器模式的核心; - 适配者类(Adaptee):
适配者类
就是被适配的类,它是一个已经存在的接口,一般是一个具体的类,是客户端希望调用的方法;
3. UML图
- 对象适配器

- 类适配器

4. 实现
- 对象适配器
csharp
// 目标抽象类
class abstract Target {
public abstract void method();
}
csharp
// 适配者类
class Apaptee {
public void othermethod() {
// 具体的实现逻辑
}
}
scala
// 适配器类
class Adapter extends Target {
private Adaptee adaptee; // 适配器中关联适配者
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
// 适配器中可以直接调用适配者的具体方法
public void methed() {
adaptee.othermethod()
}
}
- 类适配器
csharp
// 由于java是单继承,所以目标抽象类不能是抽象类,只能是接口
interface Target {
void method();
}
csharp
// 适配者类
class Apaptee {
public void othermethod() {
// 具体的实现逻辑
}
}
scala
// 适配器类
class Adapter extends Adaptee implements Target {
public void methed() {
othermethod() // 由于继承了Adaptee类,可以直接使用othermethod方法
}
}
5. 案例分析
我们实现上述的A公司想要新增支付宝支付的功能实例代码。
0️⃣ 公司之前的支付接口代码
csharp
public interface PayService {
void pay();
}
1️⃣ 支付宝支付接口代码
csharp
public class AlipayService {
void alipay() {
System.out.println("支付宝支付");
}
}
2️⃣ 引入适配者类的代码
java
public class AlipayServiceAdapter implements PayService{
private AlipayService alipayService;
public AlipayServiceAdapter(AlipayService alipayService) {
this.alipayService = alipayService;
}
@Override
public void pay() {
alipayService.alipay();
}
}
3️⃣ 客户端调用代码
java
public class Client {
public static void main(String[] args) {
AlipayService alipayService = new AlipayService();
PayService payService = new AlipayServiceAdapter(alipayService);
payService.pay();
}
}
✍ 上述代码使用了对象适配器模式,大家可以使用类适配器模式实现相同功能哦~
6. 优缺点
✅️ 优点:
目标类和适配类解耦。
引入适配器类重用现有的类就可以新增新的功能接口,无需修改原来的结构;单一职责。
将接口或者数据的转换代码从系统的主要业务分离出来;
❌ 缺点:
类适配器模式使用限制。
由于Java语言不支持多继承,一次只能适配一个适配者类,不能适配多个;增加代码负责度。
因为需要新增接口和类,比直接修改已有代码更复杂;
7. 使用场景
- 系统需要使用现有类,而这些类和工程现有代码不兼容;
- 复用一些类,它们处于同一继承体系,并且它们又有一些公共方法。
8. 扩展
8.1 双向适配器模式
双向适配器模式
是适配器模式的一种变体,它允许两个不兼容的接口之间进行双向适配,就是适配器类中,支持对目标类和适配者的引用,这个适配器就是双向适配器
。
📚 B公司需要开发一个通信接口,它们之间需要使用双向适配器模式,下面是实现的代码。
typescript
public interface ServiceInterface {
void sendMsgToClient(String msg);
}
public interface ClientInterface {
void sendMsgToService(String msg);
}
public class Service implements ServiceInterface{
@Override
public void sendMsgToClient(String msg) {
System.out.println("Service 接受到消息:" + msg);
}
}
public class Client implements ServiceInterface{
@Override
public void sendMsgToClient(String msg) {
System.out.println("Client 接收到消息:" + msg);
}
}
typescript
public class BidirectionalAdapter implements ServiceInterface, ClientInterface{
private Service service;
private Client client;
public BidirectionalAdapter(Service service, Client client) {
this.service = service;
this.client = client;
}
@Override
public void sendMsgToService(String msg) {
service.sendMsgToClient(msg);
}
@Override
public void sendMsgToClient(String msg) {
client.sendMsgToClient(msg);
}
}
java
public class Main {
public static void main(String[] args) {
Service service = new Service();
Client client = new Client();
BidirectionalAdapter adapter = new BidirectionalAdapter(service, client);
adapter.sendMsgToClient("service 的消息");
adapter.sendMsgToService("client 的消息");
}
}
🪐 运行结果:

8.2 缺省适配器模式
缺省适配器模式
也是适配器模式的一种变体,其应用比较广泛。当不需要实现一个接口提供的所有方法时,可先设计一个抽象类实现该接口,并为每一个方法提供一个空方法来覆盖父类的方法,那么抽象类的子类可以自己选择覆盖父类的方法实现需求。
缺省适配器模式
适用于不想使用接口中的所有方法的情况。
缺省适配器模式中,包含3个角色:
- 适配者接口(ServiceInterface): 是一个接口,定义了大量的方法;
- 缺省适配器(DefaultAdapter): 缺省适配器的核心,使用空方法的实现了在适配者接口中声明的方法,通常为抽象类;
- 具体业务(ConcreteService): 是缺省适配器类的子类,如果直接实现适配者接口,需要实现所有的方法,如果继承了缺省适配器,则只需要适配想要使用的方法;
📚 C公司需要开发一个界面显示的功能,定义了一个接口
ViewService
,里面定义了很多的方法,现在需要实现在某平台的显示功能,只需要实现其中的一部分方法,如果我们直接继承了ViewService
,则需要实现所有的方法,其他方法为空方法,现在我们可以引入一个缺省适配器类ViewAdapter
类,在后续增加功能时,只需要继承ViewAdapter
类即可。请看下面代码
csharp
public interface ViewService {
void showBanner();
void showColor();
void showButton();
void showCmd();
void showStyles();
void showShape();
}
less
public abstract class ViewAdapter implements ViewService{
@Override
public void showBanner() {
}
@Override
public void showColor() {
}
@Override
public void showButton() {
}
@Override
public void showCmd() {
}
@Override
public void showStyles() {
}
@Override
public void showShape() {
}
}
scala
public class LinuxViewService extends ViewAdapter{
// 只需要实现需要使用的方法
@Override
public void showCmd() {
System.out.println("展示cmd框");
}
}
9. 总结
好了,我们这篇文章到此就要告一段落了,通过这篇文章,我们学会使用了适配器模式,以后如果在项目中遇到接口不兼容的问题,就要先考虑一下适配器模式,相信我们会写出更好的代码~
欢迎朋友们关注我的公众号📢📢📢:【码匠er】
我会持续更新关于技术的文章❤️🤎💚🧡💛
欢迎大家点赞👍 收藏 ⭐ 关注 💡三连支持一下~~~
查看文章过程中有问题或者有需要修改的地方,欢迎私聊我哦 🗨🗨🗨
不管世界变成什么样,我们都要加强自己自身能力~✊✊✊