做者:小傅哥
博客:https://bugstack.cnhtml
沉淀、分享、成长,让本身和他人都能有所收获!😄
擦屁屁纸80%的面积都是保护手的!
java
工做到3年左右很大一部分程序员都想提高本身的技术栈,开始尝试去阅读一些源码,例如Spring
、Mybaits
、Dubbo
等,但读着读着发现愈来愈难懂,一会从这过来一会跑到那去。甚至怀疑本身技术太差,慢慢也就不肯意再触碰这部分知识。程序员
而这主要的缘由是一个框架随着时间的发展,它的复杂程度是愈来愈高的,从最开始只有一个很是核心的点到最后开枝散叶。这就像你本身开发的业务代码或者某个组件同样,最开始的那部分核心代码也许只能占到20%,而其余大部分代码都是为了保证核心流程能正常运行的。因此这也是你读源码费劲的一部分缘由。数据库
框架中用到了设计模式吗?
json
框架中不只用到设计模式还用了不少,并且有些时候根本不是一个模式的单独使用,而是多种设计模式的综合运用。与大部分小伙伴平时开发的CRUD可就不同了,若是都是if语句从上到下,也就算得不上什么框架了。就像你到Spring的源码中搜关键字Adapter
,就会出现不少实现类,例如;UserCredentialsDataSourceAdapter
。而这种设计模式就是咱们本文要介绍的适配器模式。设计模式
适配器在生活里随处可见
框架
若是提到在平常生活中就不少适配器的存在你会想到什么?在没有看后文以前能够先思考下。ide
bugstack虫洞栈
,回复源码下载
获取(打开获取的连接,找到序号18)工程 | 描述 |
---|---|
itstack-demo-design-6-00 | 场景模拟工程;模拟多个MQ消息体 |
itstack-demo-design-6-01 | 使用一坨代码实现业务需求 |
itstack-demo-design-6-02 | 经过设计模式优化改造代码,产生对比性从而学习 |
适配器模式的主要做用就是把本来不兼容的接口,经过适配修改作到统一。使得用户方便使用,就像咱们提到的万能充、数据线、MAC笔记本的转换头、出国旅游买个插座等等,他们都是为了适配各类不一样的口
,作的兼容。。单元测试
除了咱们生活中出现的各类适配的场景,那么在业务开发中呢?学习
在业务开发中咱们会常常的须要作不一样接口的兼容,尤为是中台服务,中台须要把各个业务线的各类类型服务作统一包装,再对外提供接口进行使用。而这在咱们日常的开发中也是很是常见的。
随着公司的业务的不断发展,当基础的系统逐步成型之后。业务运营就须要开始作用户的拉新和促活,从而保障DAU
的增速以及最终ROI
转换。
而这时候就会须要作一些营销系统,大部分常见的都是裂变、拉客,例如;你邀请一个用户开户、或者邀请一个用户下单,那么平台就会给你返利,多邀多得。同时随着拉新的量愈来愈多开始设置每个月下单都会给首单奖励,等等,各类营销场景。
那么这个时候作这样一个系统就会接收各类各样的MQ消息或者接口,若是一个个的去开发,就会耗费很大的成本,同时对于后期的拓展也有必定的难度。此时就会但愿有一个系统能够配置一下就把外部的MQ接入进行,这些MQ就像上面提到的多是一些注册开户消息、商品下单消息等等。
而适配器的思想方式也偏偏能够运用到这里,而且我想强调一下,适配器不仅是能够适配接口每每还能够适配一些属性信息。
itstack-demo-design-6-00 └── src └── main └── java └── org.itstack.demo.design ├── mq │ ├── create_account.java │ ├── OrderMq.java │ └── POPOrderDelivered.java └── service ├── OrderServicejava └── POPOrderService.java
public class create_account { private String number; // 开户编号 private String address; // 开户地 private Date accountDate; // 开户时间 private String desc; // 开户描述 // ... get/set }
public class OrderMq { private String uid; // 用户ID private String sku; // 商品 private String orderId; // 订单ID private Date createOrderTime; // 下单时间 // ... get/set }
public class POPOrderDelivered { private String uId; // 用户ID private String orderId; // 订单号 private Date orderTime; // 下单时间 private Date sku; // 商品 private Date skuName; // 商品名称 private BigDecimal decimal; // 金额 // ... get/set }
public class OrderService { private Logger logger = LoggerFactory.getLogger(POPOrderService.class); public long queryUserOrderCount(String userId){ logger.info("自营商家,查询用户的订单是否为首单:{}", userId); return 10L; } }
public class POPOrderService { private Logger logger = LoggerFactory.getLogger(POPOrderService.class); public boolean isFirstOrder(String uId) { logger.info("POP商家,查询用户的订单是否为首单:{}", uId); return true; } }
其实大部分时候接MQ消息都是建立一个类用于消费,经过转换他的MQ消息属性给本身的方法。
咱们接下来也是先体现一下这种方式的实现模拟,可是这样的实现有一个很大的问题就是,当MQ消息愈来愈多后,甚至几十几百之后,你做为中台要怎么优化呢?
itstack-demo-design-6-01 └── src └── main └── java └── org.itstack.demo.design └── create_accountMqService.java └── OrderMqService.java └── POPOrderDeliveredService.java
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后,在配置不一样的消费类时,若是不但愿一个个开发类,那么可使用代理类的方式进行处理。
itstack-demo-design-6-02 └── src └── main └── java └── org.itstack.demo.design ├── impl │ ├── InsideOrderService.java │ └── POPOrderAdapterServiceImpl.java ├── MQAdapter,java ├── OrderAdapterService,java └── RebateInfo,java
适配器模型结构
public class RebateInfo { private String userId; // 用户ID private String bizId; // 业务ID private Date bizTime; // 业务时间 private String desc; // 业务描述 // ... get/set }
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; } }
用户ID;uId
,映射到咱们须要的;userId
,作统一处理。Map<String, String> link
,也就是准确的描述了,当前MQ中某个属性名称,映射为咱们的某个属性名称。mq
消息基本都是json
格式,能够转换为MAP结构。最后使用反射调用的方式给咱们的类型赋值。@Test public void test_MQAdapter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { create_account create_account = new create_account(); create_account.setNumber("100001"); create_account.setAddress("河北省.廊坊市.广阳区.大学里职业技术学院"); create_account.setAccountDate(new Date()); 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(new Date()); 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)); }
mq.create_account(适配前){"accountDate":1591024816000,"address":"河北省.廊坊市.广阳区.大学里职业技术学院","desc":"在校开户","number":"100001"} mq.create_account(适配后){"bizId":"100001","bizTime":1591077840669,"desc":"在校开户","userId":"100001"} mq.orderMq(适配前){"createOrderTime":1591024816000,"orderId":"100000890193847111","sku":"10928092093111123","uid":"100001"} mq.orderMq(适配后){"bizId":"100000890193847111","bizTime":1591077840669,"userId":"100001"} Process finished with exit code 0
就像咱们前面提到随着业务的发展,营销活动自己要修改,不能只是接了MQ就发奖励。由于此时已经拉新的愈来愈多了,须要作一些限制。
由于增长了只有首单用户才给奖励,也就是你一年或者新人或者一个月的第一单才给你奖励,而不是你以前每一次下单都给奖励。
那么就须要对此种方式进行限制,而此时MQ中并无判断首单的属性。只能经过接口进行查询,而拿到的接口以下;
接口 | 描述 |
---|---|
org.itstack.demo.design.service.OrderService.queryUserOrderCount(String userId) | 出参long,查询订单数量 |
org.itstack.demo.design.service.OrderService.POPOrderService.isFirstOrder(String uId) | 出参boolean,判断是否首单 |
public interface OrderAdapterService { boolean isFirst(String uId); }
内部商品接口
public class InsideOrderService implements OrderAdapterService { private OrderService orderService = new OrderService(); public boolean isFirst(String uId) { return orderService.queryUserOrderCount(uId) <= 1; } }
第三方商品接口
public class POPOrderAdapterServiceImpl implements OrderAdapterService { private POPOrderService popOrderService = new POPOrderService(); public boolean isFirst(String uId) { return popOrderService.isFirstOrder(uId); } }
<= 1
,以此判断是否为首单。@Test public void test_itfAdapter() { OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl(); System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001")); OrderAdapterService insideOrderService = new InsideOrderService(); System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001")); }
23:25:47.076 [main] INFO o.i.d.design.service.POPOrderService - POP商家,查询用户的订单是否为首单:100001 判断首单,接口适配(POP):true 23:25:47.079 [main] INFO o.i.d.design.service.POPOrderService - 自营商家,查询用户的订单是否为首单:100001 判断首单,接口适配(自营):false Process finished with exit code 0