欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景)

设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景)

2024/11/14 11:04:24 来源:https://blog.csdn.net/CSDN_LiMingfly/article/details/143632582  浏览:    关键词:设计模式之适配器模式(从多个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消息体中,抽取指定字段值场景 学习结束了 友友们 点点关注不迷路 老铁们!!!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com