一、适配器模式介绍
1. 解决的问题
主要解决在系统中,将一些现存对象放入新环境中,而新环境要求的接口和现存对象不匹配的问题。
2. 定义
适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。
3. 应用场景
- 希望使用某个类,但其接口与其他代码不兼容时,可以使用适配器类。
- 如果需要复用这样一些类,它们处于同一个继承体系,并且又有了额外的一些共同的方法,但是这些共同的方法不是这一继承体系中所有子类具有的共性。
二、适配器模式优缺点
1. 优点
- 单一职责原则:可以将接口或数据转换代码从程序主要业务逻辑中分离。
- 开闭原则:只要客户端代码通过客户端接口与适配器进行交互,就能在不修改现有客户端代码的情况下,在程序中添加新类型的适配器。
2. 缺点
- 代码整体复杂度增加:因为需要增加一系列接口和类,有时直接更改服务类来保证兼容可能更简单。
三、适配器模式应用实例:同步监控通知至工作群
1. 实例场景
为了更好地感知系统的运行状况,我们会在系统运行的各个链路埋下监控点,出现异常就会进行邮件通知。 那么问题来了,在我们沉浸在代码的世界里时,有时会忽略一些邮件通知,经过一系列团队内部讨论,决定将异常监控通知同步发送到我们的工作群(钉钉、微信)里,这样就能互相提醒。
工作群的消息通知需要一些鉴权等机制后才能调用通知服务,和之前系统接入的邮件通知模式完全不一致,这时,就可以用适配器模式,将工作群的通知封装后适配到监控系统的通知接口上。
2. 适配器模式实现
2.1 工程结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| adapter-pattern └─ src ├─ main │ └─ java │ └─ org.design.pattern.adapter │ ├─ model │ │ ├─ DingDingApi.java │ │ └─ MonitorMessage.java │ └─ service │ ├─ NotificationService.java │ └─ impl │ ├─ EmailNotificationServiceImpl.java │ └─ DingDingNotificationServiceImpl.java └─ test └─ java └─ org.design.pattern.adapter.test └─ NotificationTest.java
|
2.2 代码实现
2.2.1 基础类
监控消息类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Getter @Setter @AllArgsConstructor public class MonitorMessage {
private String title;
private String message; }
|
钉钉 Api 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
public class DingDingApi { private final Logger log = LoggerFactory.getLogger(DingDingApi.class);
private String $loginApi;
private String $apiKey;
public DingDingApi(String $loginApi, String $apiKey) { this.$loginApi = $loginApi; this.$apiKey = $apiKey; }
public void logIn() { log.info("登录钉钉账户成功"); }
public boolean sendMessage(String message, String charId) { boolean result = false; try { result = true; log.info("发送钉钉消息通知到群{}成功:{}", charId, message); } catch (Exception e) { log.error("发送钉钉通知失败:", e); } return result; } }
|
2.2.2 服务类
通知服务接口
1 2 3 4 5 6
|
public interface NotificationService { void send(MonitorMessage monitorMessage); }
|
邮件通知服务实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class EmailNotificationServiceImpl implements NotificationService { private final Logger log = LoggerFactory.getLogger(EmailNotificationServiceImpl.class);
@Override public void send(MonitorMessage monitorMessage) { boolean sendResponse = mail(monitorMessage); if (sendResponse) { log.info("{} message send email success", monitorMessage.getTitle()); } else { log.error("{} message send email failed", monitorMessage.getTitle()); } }
private boolean mail(MonitorMessage monitorMessage) { boolean result = true; return result; } }
|
钉钉通知服务实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class DingDingNotificationServiceImpl implements NotificationService { private final Logger log = LoggerFactory.getLogger(DingDingNotificationServiceImpl.class);
private DingDingApi dingDingApi;
private String chatId;
public DingDingNotificationServiceImpl(DingDingApi dingDingApi, String chatId) { this.dingDingApi = dingDingApi; this.chatId = chatId; }
@Override public void send(MonitorMessage monitorMessage) { this.dingDingApi.logIn();; boolean sendResponse = this.dingDingApi.sendMessage(monitorMessage.getMessage(), this.chatId); if (sendResponse) { log.info("{} send to chartId {} success", monitorMessage.getTitle(), this.chatId); } else { log.error("{} send to chartId {} failed", monitorMessage.getTitle(), this.chatId); } } }
|
2.3 测试验证
2.3.1 测试验证类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class NotificationTest { @Test public void testNotification() { MonitorMessage monitorMessage = new MonitorMessage("获取mongo数据失败", "错误信息:xxx"); NotificationService emailNotificationService = new EmailNotificationServiceImpl(); emailNotificationService.send(monitorMessage); DingDingApi dingDingApi = new DingDingApi("https://www.dingtalk.com/", "xxx"); NotificationService dingDingNotificationService = new DingDingNotificationServiceImpl(dingDingApi, "1"); dingDingNotificationService.send(monitorMessage); } }
|
2.3.2 测试结果
1 2 3 4 5 6
| 16:02:22.892 [main] INFO o.d.p.a.s.i.EmailNotificationServiceImpl - 获取mongo数据失败 message send email success 16:02:22.894 [main] INFO o.d.p.adapter.model.DingDingApi - 登录钉钉账户成功 16:02:22.894 [main] INFO o.d.p.adapter.model.DingDingApi - 发送钉钉消息通知到群1成功:错误信息:xxx 16:02:22.894 [main] INFO o.d.p.a.s.i.DingDingNotificationServiceImpl - 获取mongo数据失败 send to chartId 1 success
Process finished with exit code 0
|
四、适配器模式结构

- 客户端(Client)是包含当前程序业务逻辑的类。
- 客户端服务接口(Client Interface)描述了其他类和客户端代码合作时的协议。
- 服务(Service)中存在一些功能类(一般来自第三方或历史代码)。客户端与其接口不兼容,因此无法直接调用其功能。
- 适配器(Adapter)是一个同时和客户端和服务交互的类:在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。
- 客户端代码仅需通过接口与适配器交互即可,无需与具体的适配器类耦合。
设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。
源码地址:https://github.com/yiyufxst/design-pattern-java
参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深入设计模式:https://refactoringguru.cn/design-patterns/catalog