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

打印 上一主题 下一主题

主题 1856|帖子 1856|积分 5568

前言
工作到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
  1. package com.lm.design.mq;
  2. import com.alibaba.fastjson.JSON;
  3. import java.util.Date;
  4. /**
  5. * 开户
  6. */
  7. public class create_account {
  8.     private String number;      // 开户编号
  9.     private String address;     // 开户地
  10.     private Date accountDate;   // 开户时间
  11.     private String desc;        // 开户描述
  12.     public String getNumber() {
  13.         return number;
  14.     }
  15.     public void setNumber(String number) {
  16.         this.number = number;
  17.     }
  18.     public String getAddress() {
  19.         return address;
  20.     }
  21.     public void setAddress(String address) {
  22.         this.address = address;
  23.     }
  24.     public Date getAccountDate() {
  25.         return accountDate;
  26.     }
  27.     public void setAccountDate(Date accountDate) {
  28.         this.accountDate = accountDate;
  29.     }
  30.     public String getDesc() {
  31.         return desc;
  32.     }
  33.     public void setDesc(String desc) {
  34.         this.desc = desc;
  35.     }
  36.     @Override
  37.     public String toString() {
  38.         return JSON.toJSONString(this);
  39.     }
  40. }
复制代码
内部订单MQ
  1. package com.lm.design.mq;
  2. import com.alibaba.fastjson.JSON;
  3. import java.util.Date;
  4. public class OrderMq {
  5.     private String uid;           // 用户ID
  6.     private String sku;           // 商品
  7.     private String orderId;       // 订单ID
  8.     private Date createOrderTime; // 下单时间
  9.     public String getUid() {
  10.         return uid;
  11.     }
  12.     public void setUid(String uid) {
  13.         this.uid = uid;
  14.     }
  15.     public String getSku() {
  16.         return sku;
  17.     }
  18.     public void setSku(String sku) {
  19.         this.sku = sku;
  20.     }
  21.     public String getOrderId() {
  22.         return orderId;
  23.     }
  24.     public void setOrderId(String orderId) {
  25.         this.orderId = orderId;
  26.     }
  27.     public Date getCreateOrderTime() {
  28.         return createOrderTime;
  29.     }
  30.     public void setCreateOrderTime(Date createOrderTime) {
  31.         this.createOrderTime = createOrderTime;
  32.     }
  33.     @Override
  34.     public String toString() {
  35.         return JSON.toJSONString(this);
  36.     }
  37. }
复制代码
第三方订单MQ
  1. package com.lm.design.mq;
  2. import com.alibaba.fastjson.JSON;
  3. import java.math.BigDecimal;
  4. import java.util.Date;
  5. /**
  6. * 订单妥投消息
  7. */
  8. public class POPOrderDelivered {
  9.     private String uId;     // 用户ID
  10.     private String orderId; // 订单号
  11.     private Date orderTime; // 下单时间
  12.     private Date sku;       // 商品
  13.     private Date skuName;   // 商品名称
  14.     private BigDecimal decimal; // 金额
  15.     public String getuId() {
  16.         return uId;
  17.     }
  18.     public void setuId(String uId) {
  19.         this.uId = uId;
  20.     }
  21.     public String getOrderId() {
  22.         return orderId;
  23.     }
  24.     public void setOrderId(String orderId) {
  25.         this.orderId = orderId;
  26.     }
  27.     public Date getOrderTime() {
  28.         return orderTime;
  29.     }
  30.     public void setOrderTime(Date orderTime) {
  31.         this.orderTime = orderTime;
  32.     }
  33.     public Date getSku() {
  34.         return sku;
  35.     }
  36.     public void setSku(Date sku) {
  37.         this.sku = sku;
  38.     }
  39.     public Date getSkuName() {
  40.         return skuName;
  41.     }
  42.     public void setSkuName(Date skuName) {
  43.         this.skuName = skuName;
  44.     }
  45.     public BigDecimal getDecimal() {
  46.         return decimal;
  47.     }
  48.     public void setDecimal(BigDecimal decimal) {
  49.         this.decimal = decimal;
  50.     }
  51.     @Override
  52.     public String toString() {
  53.         return JSON.toJSONString(this);
  54.     }
  55. }
复制代码
查询用户内部下单数量接口
  1. package com.lm.design.service;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. public class OrderService {
  5.     private Logger logger = LoggerFactory.getLogger(POPOrderService.class);
  6.     public long queryUserOrderCount(String userId){
  7.         logger.info("自营商家,查询用户的订单是否为首单:{}", userId);
  8.         return 10L;
  9.     }
  10. }
复制代码
查询用户第三方下单首单接口
  1. package com.lm.design.service;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. public class POPOrderService {
  5.     private Logger logger = LoggerFactory.getLogger(POPOrderService.class);
  6.     public boolean isFirstOrder(String uId) {
  7.         logger.info("POP商家,查询用户的订单是否为首单:{}", uId);
  8.         return true;
  9.     }
  10. }
复制代码
用一坨坨代码实现

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


这里包括了两个类型的适配:接口适配、MQ适配。之所以不只是模拟接口适配,由于很多时间大家都很常见了,所以把适配的思想换一下到MQ消息体上,增加大家多设计模式的认知。
先是做MQ适配,吸收各种各样的MQ消息。当业务发展的很快,需要对下单用户首单才给奖励,在如许的场景下再增加对接口的适配操纵。
MQ消息适配
统一的MQ消息体
  1. package com.lm.design;
  2. import java.util.Date;
  3. public class RebateInfo {
  4.     private String userId;  // 用户ID
  5.     private String bizId;   // 业务ID
  6.     private Date bizTime;   // 业务时间
  7.     private String desc;    // 业务描述
  8.     public String getUserId() {
  9.         return userId;
  10.     }
  11.     public void setUserId(String userId) {
  12.         this.userId = userId;
  13.     }
  14.     public String getBizId() {
  15.         return bizId;
  16.     }
  17.     public void setBizId(String bizId) {
  18.         this.bizId = bizId;
  19.     }
  20.     public Date getBizTime() {
  21.         return bizTime;
  22.     }
  23.     public void setBizTime(Date bizTime) {
  24.         this.bizTime = bizTime;
  25.     }
  26.     public void setBizTime(String bizTime) {
  27.         this.bizTime = new Date(Long.parseLong("1591077840669"));
  28.     }
  29.     public String getDesc() {
  30.         return desc;
  31.     }
  32.     public void setDesc(String desc) {
  33.         this.desc = desc;
  34.     }
  35. }
复制代码
MQ消息中会有多种多样的类型属性,虽然他们都有同样的值提供给利用方,但是如果都如许接入那么当MQ消息特殊多时间就会很麻烦。
MQ消息体适配类
  1. package com.lm.design;
  2. import com.alibaba.fastjson.JSON;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.util.Map;
  5. public class MQAdapter {
  6.     public static RebateInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  7.         return filter(JSON.parseObject(strJson, Map.class), link);
  8.     }
  9.     public static RebateInfo filter(Map obj, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  10.         RebateInfo rebateInfo = new RebateInfo();
  11.         for (String key : link.keySet()) {
  12.             Object val = obj.get(link.get(key));
  13.             RebateInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class).invoke(rebateInfo, val.toString());
  14.         }
  15.         return rebateInfo;
  16.     }
  17. }
复制代码
这个类里的方法非常重要,主要用于把不同类型MQ种的各种属性,映射成我们需要的属性并返回。就像一个属性中有用户ID;uId,映射到我们需要的 userId,做统一处置处罚。
而在这个处置处罚过程中需要把映射管理通报给Map<String, String> link,也就是准确的描述了,当前MQ中某个属性名称,映射为我们的某个属性名称。
终极由于我们吸收到的mq消息根本都是json格式,可以转换为MAP结构。最后利用反射调用的方式给我们的类型赋值。
编写单位测试
  1. package com.lm.design.test;
  2. import com.alibaba.fastjson.JSON;
  3. import com.lm.design.MQAdapter;
  4. import com.lm.design.OrderAdapterService;
  5. import com.lm.design.RebateInfo;
  6. import com.lm.design.cuisine.impl.InsideOrderService;
  7. import com.lm.design.cuisine.impl.POPOrderAdapterServiceImpl;
  8. import com.lm.design.mq.OrderMq;
  9. import com.lm.design.mq.create_account;
  10. import org.junit.Test;
  11. import java.lang.reflect.InvocationTargetException;
  12. import java.text.ParseException;
  13. import java.text.SimpleDateFormat;
  14. import java.util.Date;
  15. import java.util.HashMap;
  16. public class ApiTest {
  17.     @Test
  18.     public void test_MQAdapter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ParseException {
  19.         SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  20.         Date parse = s.parse("2020-06-01 23:20:16");
  21.         create_account create_account = new create_account();
  22.         create_account.setNumber("100001");
  23.         create_account.setAddress("河北省.廊坊市.广阳区.大学里职业技术学院");
  24.         create_account.setAccountDate(parse);
  25.         create_account.setDesc("在校开户");
  26.         HashMap<String, String> link01 = new HashMap<String, String>();
  27.         link01.put("userId", "number");
  28.         link01.put("bizId", "number");
  29.         link01.put("bizTime", "accountDate");
  30.         link01.put("desc", "desc");
  31.         RebateInfo rebateInfo01 = MQAdapter.filter(create_account.toString(), link01);
  32.         System.out.println("mq.create_account(适配前)" + create_account.toString());
  33.         System.out.println("mq.create_account(适配后)" + JSON.toJSONString(rebateInfo01));
  34.         System.out.println("");
  35.         OrderMq orderMq = new OrderMq();
  36.         orderMq.setUid("100001");
  37.         orderMq.setSku("10928092093111123");
  38.         orderMq.setOrderId("100000890193847111");
  39.         orderMq.setCreateOrderTime(parse);
  40.         HashMap<String, String> link02 = new HashMap<String, String>();
  41.         link02.put("userId", "uid");
  42.         link02.put("bizId", "orderId");
  43.         link02.put("bizTime", "createOrderTime");
  44.         RebateInfo rebateInfo02 = MQAdapter.filter(orderMq.toString(), link02);
  45.         System.out.println("mq.orderMq(适配前)" + orderMq.toString());
  46.         System.out.println("mq.orderMq(适配后)" + JSON.toJSONString(rebateInfo02));
  47.     }
  48.     @Test
  49.     public void test_itfAdapter() {
  50.         OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();
  51.         System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001"));
  52.         OrderAdapterService insideOrderService = new InsideOrderService();
  53.         System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001"));
  54.     }
  55. }
复制代码
在这里我们分别模拟传入了两个不同的MQ消息,并设置字段的映射关系。
总结
1:到不利用适配器模式这些功能同样可以实现,但是利用了适配器模式就可以让代码:干净整洁易于维护、镌汰大量重复的判断和利用、让代码更加易于维护和拓展。
2:尤其是我们对MQ如许的多种消息体中不同属性同类的值,进行适配再加上代理类,就可以利用简单的配置方式接入对方提供的MQ消息,而不需要大量重复的开辟。非常利于拓展。
好了 至此 设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景 学习结束了 友友们 点点关注不迷路 老铁们!!!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

石小疯

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表