设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景)
前言
工作到3年左右很大一部分程序员都想提升自己的技术栈,开始尝试去阅读一些源码,例如Spring、Mybaits、Dubbo等,但读着读着发现越来越难懂,一会从这过来一会跑到那去。甚至怀疑自己技术太差,慢慢也就不愿意再触碰这部分知识。
框架中用到了设计模式吗?
架中不仅用到设计模式还用了很多,而且有些时候根本不是一个模式的单独使用,而是多种设计模式的综合运用。与大部分小伙伴平时开发的CRUD可就不一样了,如果都是if语句从上到下,也就算得不上什么框架了。就像你到Spring的源码中搜关键字Adapter,就会出现很多实现类,例如 UserCredentialsDataSourceAdapter。而这种设计模式就是我们本文要介绍的适配器模式。
适配器模式

适配器模式的主要作用就是把原本不兼容的接口,通过适配修改做到统一。使得用户方便使用,就像我们提到的万能充、数据线、MAC笔记本的转换头、出国旅游买个插座等等,他们都是为了适配各种不同的口,做的兼容。。

在业务开发中我们会经常的需要做不同接口的兼容,尤其是中台服务,中台需要把各个业务线的各种类型服务做统一包装,再对外提供接口进行使用。而这在我们平常的开发中也是非常常见的。
做一些营销系统,大部分常见的都是裂变、拉客,例如:你邀请一个用户开户、或者邀请一个用户下单,那么平台就会给你返利,多邀多得。同时随着拉新的量越来越多开始设置每月下单都会给首单奖励,等等,各种营销场景。
那么这个时候做这样一个系统就会接收各种各样的MQ消息或者接口,如果一个个的去开发,就会耗费很大的成本,同时对于后期的拓展也有一定的难度。此时就会希望有一个系统可以配置一下就把外部的MQ接入进行,这些MQ就像上面提到的可能是一些注册开户消息、商品下单消息等等。

1:这里模拟了三个不同类型的MQ消息,而在消息体中都有一些必要的字段,比如;用户ID、时间、业务ID,但是每个MQ的字段属性并不一样。就像用户ID在不同的MQ里也有不同的字段:uId、userId等。
2:同时还提供了两个不同类型的接口,一个用于查询内部订单订单下单数量,一个用于查询第三方是否首单。
3:后面会把这些不同类型的MQ和接口做适配兼容。
注册开户MQ
package com.lm.design.mq;import com.alibaba.fastjson.JSON;import java.util.Date;/*** 开户*/
public class create_account {private String number; // 开户编号private String address; // 开户地private Date accountDate; // 开户时间private String desc; // 开户描述public String getNumber() {return number;}public void setNumber(String number) {this.number = number;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public Date getAccountDate() {return accountDate;}public void setAccountDate(Date accountDate) {this.accountDate = accountDate;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}
内部订单MQ
package com.lm.design.mq;import com.alibaba.fastjson.JSON;import java.util.Date;public class OrderMq {private String uid; // 用户IDprivate String sku; // 商品private String orderId; // 订单IDprivate Date createOrderTime; // 下单时间public String getUid() {return uid;}public void setUid(String uid) {this.uid = uid;}public String getSku() {return sku;}public void setSku(String sku) {this.sku = sku;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public Date getCreateOrderTime() {return createOrderTime;}public void setCreateOrderTime(Date createOrderTime) {this.createOrderTime = createOrderTime;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}
第三方订单MQ
package com.lm.design.mq;import com.alibaba.fastjson.JSON;import java.math.BigDecimal;
import java.util.Date;/*** 订单妥投消息*/
public class POPOrderDelivered {private String uId; // 用户IDprivate String orderId; // 订单号private Date orderTime; // 下单时间private Date sku; // 商品private Date skuName; // 商品名称private BigDecimal decimal; // 金额public String getuId() {return uId;}public void setuId(String uId) {this.uId = uId;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public Date getOrderTime() {return orderTime;}public void setOrderTime(Date orderTime) {this.orderTime = orderTime;}public Date getSku() {return sku;}public void setSku(Date sku) {this.sku = sku;}public Date getSkuName() {return skuName;}public void setSkuName(Date skuName) {this.skuName = skuName;}public BigDecimal getDecimal() {return decimal;}public void setDecimal(BigDecimal decimal) {this.decimal = decimal;}@Overridepublic String toString() {return JSON.toJSONString(this);}}
查询用户内部下单数量接口
package com.lm.design.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class OrderService {private Logger logger = LoggerFactory.getLogger(POPOrderService.class);public long queryUserOrderCount(String userId){logger.info("自营商家,查询用户的订单是否为首单:{}", userId);return 10L;}}
查询用户第三方下单首单接口
package com.lm.design.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class POPOrderService {private Logger logger = LoggerFactory.getLogger(POPOrderService.class);public boolean isFirstOrder(String uId) {logger.info("POP商家,查询用户的订单是否为首单:{}", uId);return true;}}
用一坨坨代码实现

Mq接收消息实现
package com.lm.design;import com.alibaba.fastjson.JSON;
import com.lm.design.mq.create_account;public class create_accountMqService {public void onMessage(String message) {create_account mq = JSON.parseObject(message, create_account.class);mq.getNumber();mq.getAccountDate();// ... 处理自己的业务}}
适配器模式重构代码
适配器模式要解决的主要问题就是多种差异化类型的接口做统一输出,这在我们学习工厂方法模式中也有所提到不同种类的奖品处理,其实那也是适配器的应用。
在本文中我们还会再另外体现出一个多种MQ接收,使用MQ的场景。来把不同类型的消息做统一的处理,便于减少后续对MQ接收。


这里包括了两个类型的适配:接口适配、MQ适配。之所以不只是模拟接口适配,因为很多时候大家都很常见了,所以把适配的思想换一下到MQ消息体上,增加大家多设计模式的认知。
先是做MQ适配,接收各种各样的MQ消息。当业务发展的很快,需要对下单用户首单才给奖励,在这样的场景下再增加对接口的适配操作。
MQ消息适配
统一的MQ消息体
package com.lm.design;import java.util.Date;public class RebateInfo {private String userId; // 用户IDprivate String bizId; // 业务IDprivate Date bizTime; // 业务时间private String desc; // 业务描述public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getBizId() {return bizId;}public void setBizId(String bizId) {this.bizId = bizId;}public Date getBizTime() {return bizTime;}public void setBizTime(Date bizTime) {this.bizTime = bizTime;}public void setBizTime(String bizTime) {this.bizTime = new Date(Long.parseLong("1591077840669"));}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}
}
MQ消息中会有多种多样的类型属性,虽然他们都有同样的值提供给使用方,但是如果都这样接入那么当MQ消息特别多时候就会很麻烦。
MQ消息体适配类
package com.lm.design;import com.alibaba.fastjson.JSON;import java.lang.reflect.InvocationTargetException;
import java.util.Map;public class MQAdapter {public static RebateInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {return filter(JSON.parseObject(strJson, Map.class), link);}public static RebateInfo filter(Map obj, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {RebateInfo rebateInfo = new RebateInfo();for (String key : link.keySet()) {Object val = obj.get(link.get(key));RebateInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class).invoke(rebateInfo, val.toString());}return rebateInfo;}}
这个类里的方法非常重要,主要用于把不同类型MQ种的各种属性,映射成我们需要的属性并返回。就像一个属性中有用户ID;uId,映射到我们需要的 userId,做统一处理。
而在这个处理过程中需要把映射管理传递给Map<String, String> link,也就是准确的描述了,当前MQ中某个属性名称,映射为我们的某个属性名称。
最终因为我们接收到的mq消息基本都是json格式,可以转换为MAP结构。最后使用反射调用的方式给我们的类型赋值。
编写单元测试
package com.lm.design.test;import com.alibaba.fastjson.JSON;
import com.lm.design.MQAdapter;
import com.lm.design.OrderAdapterService;
import com.lm.design.RebateInfo;
import com.lm.design.cuisine.impl.InsideOrderService;
import com.lm.design.cuisine.impl.POPOrderAdapterServiceImpl;
import com.lm.design.mq.OrderMq;
import com.lm.design.mq.create_account;
import org.junit.Test;import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;public class ApiTest {@Testpublic void test_MQAdapter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ParseException {SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date parse = s.parse("2020-06-01 23:20:16");create_account create_account = new create_account();create_account.setNumber("100001");create_account.setAddress("河北省.廊坊市.广阳区.大学里职业技术学院");create_account.setAccountDate(parse);create_account.setDesc("在校开户");HashMap<String, String> link01 = new HashMap<String, String>();link01.put("userId", "number");link01.put("bizId", "number");link01.put("bizTime", "accountDate");link01.put("desc", "desc");RebateInfo rebateInfo01 = MQAdapter.filter(create_account.toString(), link01);System.out.println("mq.create_account(适配前)" + create_account.toString());System.out.println("mq.create_account(适配后)" + JSON.toJSONString(rebateInfo01));System.out.println("");OrderMq orderMq = new OrderMq();orderMq.setUid("100001");orderMq.setSku("10928092093111123");orderMq.setOrderId("100000890193847111");orderMq.setCreateOrderTime(parse);HashMap<String, String> link02 = new HashMap<String, String>();link02.put("userId", "uid");link02.put("bizId", "orderId");link02.put("bizTime", "createOrderTime");RebateInfo rebateInfo02 = MQAdapter.filter(orderMq.toString(), link02);System.out.println("mq.orderMq(适配前)" + orderMq.toString());System.out.println("mq.orderMq(适配后)" + JSON.toJSONString(rebateInfo02));}@Testpublic void test_itfAdapter() {OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001"));OrderAdapterService insideOrderService = new InsideOrderService();System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001"));}}
在这里我们分别模拟传入了两个不同的MQ消息,并设置字段的映射关系。
总结
1:到不使用适配器模式这些功能同样可以实现,但是使用了适配器模式就可以让代码:干净整洁易于维护、减少大量重复的判断和使用、让代码更加易于维护和拓展。
2:尤其是我们对MQ这样的多种消息体中不同属性同类的值,进行适配再加上代理类,就可以使用简单的配置方式接入对方提供的MQ消息,而不需要大量重复的开发。非常利于拓展。
好了 至此 设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景 学习结束了 友友们 点点关注不迷路 老铁们!!!!!
相关文章:
设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景)
前言 工作到3年左右很大一部分程序员都想提升自己的技术栈,开始尝试去阅读一些源码,例如Spring、Mybaits、Dubbo等,但读着读着发现越来越难懂,一会从这过来一会跑到那去。甚至怀疑自己技术太差,慢慢也就不愿意再触碰这…...
vue+exceljs前端下载、导出xlsx文件
首先安装插件 npm install exceljs file-saver第一种 简单导出 //页面引入 import ExcelJS from exceljs; import {saveAs} from file-saver; export default {methods: { /** 导出操作 */async handleExportFun() {let that this// 获取当前年月日 用户下载xlsx的文件名称设…...
算法定制LiteAIServer摄像机实时接入分析平台烟火检测算法的主要功能
在现代社会,随着人工智能技术的飞速发展,智能监控系统在公共安全领域的应用日益广泛。其中,烟火检测作为预防火灾的重要手段,其准确性和实时性对于减少火灾损失、保障人民生命财产安全具有重要意义。而算法定制LiteAIServer烟火检…...
用 Python 从零开始创建神经网络(二)
用 Python 从零开始创建神经网络(二) 引言1. Tensors, Arrays and Vectors:2. Dot Product and Vector Additiona. Dot Product (点积)b. Vector Addition (向量加法) 3. A Single Neuron with …...
嘉吉连续第七年亮相进博会
以“新质绿动,共赢未来”为主题,嘉吉连续第七年亮相进博会舞台。嘉吉带来了超过120款产品与解决方案,展示嘉吉在农业、食品、金融和工业等领域以客户为中心的创新成果。这些产品融合了嘉吉在相关领域的前瞻性思考,以及对本土市场的…...
设计模式之单列模式(7种单例模式案例,Effective Java 作者推荐枚举单例模式)
前言 在设计模式中按照不同的处理方式共包含三大类;创建型模式、结构型模式和行为模式,其中创建型模式目前已经介绍了其中的四个;工厂方法模式、抽象工厂模式、生成器模式和原型模式,除此之外还有最后一个单例模式。 单列模式介绍…...
多个服务器共享同一个Redis Cluster集群,并且可以使用Redisson分布式锁
Redisson 是一个高级的 Redis 客户端,它支持多种分布式 Java 对象和服务。其中之一就是分布式锁(RLock),它可以跨多个应用实例在多个服务器上使用同一个 Redis 集群,为这些实例提供锁服务。 当你在不同服务器上运行的…...
100种算法【Python版】第59篇——滤波算法之扩展卡尔曼滤波
本文目录 1 算法步骤2 算法示例2.1 示例描述2.2 python代码3 算法应用:机器人位姿估计扩展卡尔曼滤波(EKF)是一种处理非线性系统的状态估计算法。它通过线性化非线性系统来实现类似于线性卡尔曼滤波的效果。 1 算法步骤 (1)初始化 初始状态: x ^ 0 ∣ 0 \hat{x}_{0|0}...
制造业数字化转型的强大赋能平台:盘古信息IMS OS工软技术底座
在制造业数字化转型的浪潮中,技术底座的选择与实施至关重要。它不仅决定了企业数字化转型的深度与广度,还影响着企业的生产效率、成本控制和市场竞争力。盘古信息IMS OS作为一款强大的工软技术底座,凭借其高度模块化、可配置的设计理念&#…...
域名+服务器+Nginx+宝塔使用SSL证书配置HTTPS
前言 在我的前面文章里,有写过一篇文章 linux服务器宝塔从头部署别人可访问的网站 在这篇文章,有教学怎么使用宝塔和买的服务器的公网IP,以及教怎么打包vue和springboot去部署不用域名的网站让别人访问 那么,这篇文章将在这个…...
UnityAssetsBundle字体优化解决方案
Unity开发某个项目,打包后的apk包体已经高达1.25G了,这是非常离谱的。为了不影响用户体验,需要将apk包体缩小。因为项目本身不包含很多模型以及其他大型资源,排除法将AB包删除,发现app本身就100多M。 由此可以锁定是AB…...
Go的环境搭建以及GoLand安装教程
目录 一、开发环境Golang安装 二、配置环境变量 三、GoLand安装 四、Go 语言的 Hello World 一、开发环境Golang安装 官方网址: The Go Programming Language 1. 首先进入官网,点击Download,选择版本并进行下载: 2. …...
git clone,用https还是ssh
前言 在使用Git去克隆项目时,会遇到https和ssh等形式,这两种又有何种区别呢,本文将重点讨论在具体使用中的问题。 注:第一次使用Git 时,需要先设置全局用户名和邮箱,否则后续使用命令时会报错,也是提醒先添…...
量化交易系统开发-实时行情自动化交易-Okex行情交易数据
19年创业做过一年的量化交易但没有成功,作为交易系统的开发人员积累了一些经验,最近想重新研究交易系统,一边整理一边写出来一些思考供大家参考,也希望跟做量化的朋友有更多的交流和合作。 接下来聊聊基于Okex交易所API获取行情数…...
【重装系统后重新配置2】pycharm 终端无法激活conda环境
pycharm 终端无法激活 conda 环境,但是 Windows本地终端是可以激活的 原因是pycharm 默认的终端是 Windows PowerShell 解决方法有两个: 一、在设置里,修改为cmd 二、下面直接选择...
【LeetCode每日一题】——802.找到最终的安全状态
文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 图 二【题目难度】 中等 三【题目编号】 802.找到最终的安全状态 四【题目描述】 有一个有…...
kafka安装部署--详细教程
2.1 安装部署 每次进入 linux 都会自动进入 base 环境,如何关闭 base conda deactivate 手动关闭 conda config --set auto_activate_base false 关闭自动进入 2.1.1 集群规划 bigdata01 bigdata02 bigdata03 zk zk zk kafka kafka kafka 2.1.2 集群部…...
CMD 查询python 出现 No pyvenv.cfg file 很奇怪 2024/11/9
CMD 查询python 出现 No pyvenv.cfg file 很奇怪 查询得到我有很多个...........版本 删除这个变量就不会 因为 没有安装软件 跳转到 Windows 商店 再把主要使用的python路径置顶 现在运行cmd查询 对比之前的图片 可以发现 这一条商店的没有了! 完整测试效果,问题解决了!...
learnopencv系列二:U2-Net/IS-Net图像分割(背景减除)算法、使用背景减除实现视频转ppt应用
文章目录 一、视频转幻灯片应用1.1 什么是背景减除?1.1.1 背景减除简介1.1.2 bgslibrary 1.2 OpenCV背景减除技术1.3 差异哈希1.3.1 图像哈希技术1.3.2 dHash算法1.3.3 图像哈希的速度和准确性测试 1.4 视频转幻灯片应用的工作流程1.5 项目代码1.5.1 环境准备1.5.2 …...
linux命令详解,文件系统权限相关
文件系统权限相关 linux系统中一切都是文件 查看权限 Is -la /etc/passwd更改文件所有者 chown root file修改文件权限 sudo chmod urwx,grw,o-r file sudo chmod ux,gtw,o-r file chmod 400 <file>一、Linux系统中一切都是文件 在linux系统中,几乎所有的…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
