【设计模式】使用适配器模式做补偿设计

news2024/11/18 10:39:31

文章目录

  • 1.概述
  • 2.两种适配器模式
    • 2.1.类适配器
    • 2.2.对象适配器
  • 3.总结

1.概述

适配器模式是一种结构型设计模式,它提供了一个中间层,通过这个中间层,客户端可以使用统一的接口与具有不同接口的类进行交互,也就是说,将一个接口转换成客户期望的另一个接口,使得原本不兼容的接口能够协同工作。

举个现实中的例子,我们现在的很多轻薄笔记本为了减少厚度,一般不会设计网线的接口,或者说在笔记本上的可以插线的接口很少,这时候使用到的拓展坞就可以视为是一种适配器。
在这里插入图片描述


值得一提的是,与其他模式有点不同的是,适配器模式是一种补偿模式,主要用于解决现有的设计或实现与需求不匹配的问题,是在系统开发后期或者集成阶段,为了兼容已存在的、难以修改的组件接口或者为了统一不同接口之间的差异而采取的一种补救措施。

2.两种适配器模式

适配器模式有两种常见的实现形式:

  • 类适配器:通过继承的方式,子类(适配器)继承自需要被适配的类(适配者),并同时实现目标接口。
  • 对象适配器:通过组合的方式,适配器包含一个适配者的实例,并在自己的方法中调用适配者的功能来实现目标接口的方法。

2.1.类适配器

按照上面的描述,类图如下:
在这里插入图片描述

  • Target:目标接口,适配器将不适用于当前系统的接口,转换给目标接口的形状。
  • Adaptee:被适配的类,也就是被转换的对象。
  • Adapter:适配器

Client调用适配器,获取到转换成符合当前系统要求的数据。


这么看可能有点抽象,我们通过一个简单的业务场景来理解一下这种模式:

有一个客服系统,在了解到客户的需求后会往客户管理系统中推送线索,并且在客服系统中可以查看到销售对当前客户的跟进情况。现在由于旧客户管理系统日渐不满足使用要求了,于是建立了一个新的客户管理系统,客服系统需要从新的系统中获取到跟进数据。

但是,新旧两个客户系统对于跟进日志的接口定义不一样,这时候又不想对客服系统做大的改动,就可以使用适配器对接口进行转换,下面是简化过后的代码。

  • 旧系统的接口定义
    /**
     * 跟进记录对象
     */
    @Data
    public class Record {
        /**
         * 跟进内容
         */
        private String followContent;
        /**
         * 附件地址
         */
        private String enclosure;
    }
    
    /**
     * 目标接口
     */
    public interface RecordService {
        Record getRecord();
    }
    
    /**
     * 目标接口实现
     */
    public class RecordServiceImpl implements RecordService {
        @Override
        public Record getRecord() {
            // 模拟从老系统获取数据
            Record record = new Record();
            record.setFollowContent("跟进内容(老系统)");
            record.setEnclosure("附件地址(老系统)");
            return record;
        }
    }
    
  • 新系统中的定义
/**
 * 新系统沟通记录对象
 */
@Data
public class NewRecord {
    /**
     * 沟通内容
     */
    private String communicateContent;

    /**
     * 附件地址
     */
    private String accessory;
}

/**
 * 新系统接口
 */
public interface NewRecordService {
    NewRecord getNewRecord();
}

/**
 * 新系统接口实现
 */
public class newRecordServiceImpl implements NewRecordService{
    @Override
    public NewRecord getNewRecord() {
        // 模拟从新系统获取数据
        NewRecord newRecord = new NewRecord();
        newRecord.setCommunicateContent("跟进内容(新系统)");
        newRecord.setAccessory("附件地址(新系统)");
        return newRecord;
    }
}

实际情况相对于上面的代码可能会更加复杂,这里做演示就简化了一下代码,新旧系统主要是返回对象不一样,返回对象中的字段名不一样。

确定了之后编写适配器,按照类适配器的定义,我们先继承新接口的实现,再实现旧的模板接口

public class RecordAdapter extends newRecordServiceImpl implements RecordService {

    @Override
    public Record getRecord() {
        NewRecord newRecord = super.getNewRecord();
        Record record = new Record();
        record.setFollowContent(newRecord.getCommunicateContent());
        record.setEnclosure(newRecord.getAccessory());
        return record;
    }
}

在适配器中,实现旧接口中的方法,并调用父类(新接口实现)的新的方法,然后将新接口返回的对象值封装到旧的日志对象中,做一下测试:

public class RecordAdapterTest {
    @Test
    public void testGetRecord() {
        RecordService recordService = new RecordServiceImpl();
        System.out.println(recordService.getRecord());
        recordService = new RecordAdapter();
        System.out.println(recordService.getRecord());
    }
}

Record(followContent=跟进内容(老系统), enclosure=附件地址(老系统))
Record(followContent=跟进内容(新系统), enclosure=附件地址(新系统))

可以看到,在字段名没变动的情况下,兼容了新系统的值。

通过适配器的方式,不需要修改新系统的接口,也不需要修改客服系统的上层业务代码,只需要在获取数据这一层做一下转换即可,返回给前端后,前端也不需要重新匹配字段,减少了代码的修改范围,降低了风险。


类适配器对这种简单的转换用起来比较方便,但是也存在比较大的缺陷:

  • 继承带来的常见问题,父类发生变化时,子类可能也需要被迫的跟着变化。
  • 对于Java这样的单继承语言来说,面对有多个需要被转换的对象时,就显得有点力不从心了。

所以,在大部分情况下,尤其是使用Java语言的情况下,更建议使用对象适配器

2.2.对象适配器

相对于类适配器,对象适配器能提供更高的灵活性和更低的耦合度,原理上也比较简单,就是将继承修改为组合,也就是这样。
在这里插入图片描述

将上面的适配器代码做一下修改,如下:

public class RecordAdapter2 implements RecordService {

    private NewRecordService newRecordService;

    public RecordAdapter2(NewRecordService newRecordService) {
        this.newRecordService = newRecordService;
    }

    @Override
    public Record getRecord() {
        NewRecord newRecord = newRecordService.getNewRecord();
        Record record = new Record();
        record.setFollowContent(newRecord.getCommunicateContent());
        record.setEnclosure(newRecord.getAccessory());
        return record;
    }
}

在创建对象适配器时,将被适配的对象直接传入到适配器中即可,如果是Spring的服务,这些被适配的对象还可以自动依赖注入,也很方便。
使用对象适配器时,可以注入多个Adaptee,当目标对象需要的数据需要从多个不同的接口中查询出来再做聚合的时候,就可以使用这种方式来处理。用上面的代码来说就是,跟进记录附件地址需要从不同的接口进行查询的时候。

3.总结

本篇主要是将适配器模式的使用,作为一种补偿模式,不建议一开始就使用适配器模式,如果能够在设计初期尽可能的避免出现接口不兼容的情况,那么直接设计出符合需求的标准接口会更优。但毕竟没有完美的设计,当设计上存在一定的缺陷又没有资源做大重构的时候,适配器模式就派上用场了。

对于类适配器和对象适配器,区别就是在于类适配器通过继承实现,对象适配器通过组合实现,对于Java这样的单继承语言,更建议使用对象适配器,更加灵活。

再补充一下适配器的一些使用场景:

  • 替换依赖的系统:例如上面的那个例子
  • 接入第三方库或API:第三方的API或接口设计我们不能控制,可以用适配器将传输的报文转换成我们系统中的形式来落库。
  • 整合多个接口设计:例如一个短信服务中,对于短信发送的内容要做敏感词、黑词过滤,我们有自己的规则,不同的短信服务商也有自己的规则,可以用适配器将不同的规则整合起来,方便统一调用。
  • ……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1462335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Chromium的下载地址

Chromium的下载地址: Download Chromiumhttps://www.chromium.org/getting-involved/download-chromium/ https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefixWin_x64/https://commondatastorage.googleapis.com/chromium-br…

[设计模式Java实现附plantuml源码~行为型]协调多个对象之间的交互——中介者模式

前言: 为什么之前写过Golang 版的设计模式,还在重新写Java 版? 答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。 为什么类图要附上uml 因为很…

linux CentOs 安装docker 推荐生产环境使用

目录 1. 在CentOs上安装docker所需的系统环境 2. 卸载旧版本 2.1 查看是否已安装docker 2.2 卸载已安装的docker 3. 安装方式 3.1 使用rpm存储库安装(推荐使用该方法) 3.2 从包中安装 4. 开始docker 1. 在CentOs上安装docker所需的系统环境 需要以下CentOS版本之一的维…

基于JAVA的人事管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员功能模块2.2 普通员工功能模块2.3 答辩文案 三、系统展示四、核心代码4.1 查询职称4.2 新增留言回复4.3 工资申请4.4 工资审核4.5 员工请假 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的人…

STM32-开发板介绍

市面的开发板有很多,博主有幸了解到一款集成度较高的开发板,朗峰STM32F103RCT6,知名度不高,性价比很高,这是目前唯一一款集成了大量传感器和功能模块的高集成度开发板。 巨大的优势在于,传感器和功能模块的…

高效社区:数字孪生在智慧社区中的应用与实践

随着科技的快速发展,数字孪生技术在智慧社区建设中扮演着越来越重要的角色。数字孪生技术通过建立物理空间与数字空间的交互映射关系,实现对社区设施、环境、服务等智能化管理和服务,为未来社区的发展提供了新的动力。本文将探讨数字孪生在智…

【AI大语言模型】ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮,可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

深入理解指针(c语言)

目录 一、使用指针访问数组二、数组名的理解1、数组首元素的地址2、整个数组 三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组 一、使用指针访问数组 可以使用指针来访问数组元素。例如,可以声明一个指针变量并将其指向数组的第一个元素,然…

Leo赠书活动-17期 《基础软件之路:企业级实践及开源之路》

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 赠书活动专栏 ✨特色专栏:…

基于深度学习的连锁酒店场景识别与部署实践指南

基于深度学习的连锁酒店场景识别与部署实践指南 项目背景与意义数据集介绍数据预处理与加载模型选择与开发模型选择模型训练模型评估测试 模型部署服务 项目背景与意义 在智能旅游行业,场景识别(又称为场景分类)是一个关键技术,它…

Autosar-CanIf模块介绍

4.1CanIf模块简介 CanIf模块是为了要隔离MCal层与服务层,因此CanIf不可直接操作硬件资源。然而CanIf传输数据、接收数据要使用到Can模块的缓存,如果直接使用Can模块的缓存的话,那CanIf就与MCal层耦合在一起了。 为了解决这个问题,CanIf层使用了一个HOH的概念:Hardware Obj…

情暖冬日团建——手拉手助成长社会融合实践活动

2月21日上午合肥六中高一(14)班团支部的曹文祥、储诚羲、杜尤扬、林彦锋、古胜宇、周文涛、许君昊、郭稷豪、吴彤、徐欣悦 、 褚福磊、高彦希、李柄杉、周子翔、高杨、姚子涵和高一(3)班孙锦宸、高一(7)班朱…

Istio实战:Istio Kiali部署与验证

目录 前言一、Istio安装小插曲 注意事项 二、Kiali安装三、Istio测试参考资料 前言 前几天我就开始捣腾Istio。前几天在执行istioctl install --set profiledemo -y 的时候老是在第二步就报错了,开始我用的istio版本是1.6.8。 后面查看k8s与istio的版本对应关系后发…

FIPS 140-3认证有什么重要性

FIPS 140-3,全称为《Federal Information Processing Standards Publication 140-3》,是美国联邦政府制定的一套关于加密模块的标准。该标准旨在确保加密技术在政府、军事和商业等领域的安全性和合规性。自1994年首次发布以来,FIPS 140-3已成…

【学习总结】慢SQL治理经验总结

一、慢SQL定义 执行超过1s的SQL为慢SQL 三、慢SQl的风险 系统的响应时间延迟,影响用户体验 资源占用增加,增高了系统的负载,其他请求响应时间也可能会收到影响。 慢SQL占用数据库连接的时间长,如果有大量慢SQL查询同时执行,可能…

人工智能讲师AI讲师大模型讲师叶梓介绍及大语言模型技术原理与实践提纲

叶梓,上海交通大学计算机专业博士毕业,高级工程师。主研方向:数据挖掘、机器学习、人工智能。历任国内知名上市IT企业的AI技术总监、资深技术专家,市级行业大数据平台技术负责人。 长期负责城市信息化智能平台的建设工作&#xff…

[ai笔记11] 论ai韭菜的自我修养

欢迎来到文思源想的ai空间,这是技术老兵学习ai以及观点分享的第11篇内容! 上班之后时间确实少了许多,但是最近也没闲着,关于ai的学习一直在探索两个部分,一个是看那本有名的书《这就是ChatGPT》,另外一个则…

详细设计文档模版

背景 描述为什么要做这个技术改造,改造后收益是什么。可以是业务背景或者技术背景。 业务需求类此处可以将产品需求文档中的内容放到此处。技术改造类项目记录发起改造的原因,如系统问题、架构升级、bug修复、性能优化等。 现状 描述当前的技术实现的…

C#上位机与三菱PLC的通信09---开发自己的通讯库(A-3E版)

1、A-3E报文回顾 具体细节请看: C#上位机与三菱PLC的通信05--MC协议之QnA-3E报文解析 C#上位机与三菱PLC的通信06--MC协议之QnA-3E报文测试 2、为何要开发自己的通讯库 前面开发了自己的A-1E协议的通讯库,实现了数据的读写,对于封装的通…

合金电阻2512 0.01R是怎么应用在电池保护板中的

合金电阻2512 0.01R可以应用在电池保护板中的过流保护电路中。电池保护板用于监测和控制电池的充放电状态,以防止电池过充、过放和过流等情况,保护电池的安全和寿命。 过流保护电路是电池保护板的主要功能之一,用于检测电池输出电流是否超过安…