莱芜网站seo,手机免费图片制作软件,开发网站比较好的公司,删除自豪的采用wordpress1、引言
本文以一个实际案例来介绍在解决业务需求的路上#xff0c;如何通过常用的设计模式来逐级优化我们的代码#xff0c;以把我们所了解的到设计模式真实的应用于实战。
2、背景
假定我们现在有一个订单流程管理系统#xff0c;这个系统对于用户发起的一笔订单#…1、引言
本文以一个实际案例来介绍在解决业务需求的路上如何通过常用的设计模式来逐级优化我们的代码以把我们所了解的到设计模式真实的应用于实战。
2、背景
假定我们现在有一个订单流程管理系统这个系统对于用户发起的一笔订单需要你编写代码按照以下环节进行依次处理 “ 注本文不会对每个环节的实现细节进行描述读者也不必了解这每个环节的实现我们只需要关注代码架构设计 3、第一次迭代
按照背景我们如果不是打算if-else一撸到底的话我们最合适使用的设计模式应该是责任链模式于是我们先打算用责任链模式来做我们的第一次迭代。 先整体看下类图 我们定义一个抽象类抽象类中定义了下一个处理器方便后期我们读取配置直接构建责任链处理顺序
Data
public abstract class BizOrderHandler {/*** 下一个处理器*/private BizOrderHandler nextBizOrderHandler;/*** 处理器执行方法* param param 责任链参数* return 执行结果*/public abstract Result handle(ProductVO param);/*** 链路传递* param param* return*/protected Result next(ProductVO param) {//下一个链路没有处理器了直接返回if (Objects.isNull(nextBizOrderHandler)) {return Result.success();}//执行下一个处理器return nextBizOrderHandler.handle(param);}}然后我们将需要实现的流程都来实现这个接口 为了简单只列举一个
public class StorageCheckBizOrderHandler extends BizOrderHandler {Overridepublic Result handle(ProductVO param) {// 这里写上仓储管理的业务逻辑System.out.println(StorageCheckBizOrderHandler doing business!);return super.next(param);}
}通过调用父类的next方法实现了链式传递,接下来我们就可以使用责任链来实现业务了
public class OrderHandleCases {static MapString, BizOrderHandler handlerMap new HashMap();static {handlerMap.put(Storage, new StorageCheckBizOrderHandler());handlerMap.put(Payment, new PaymentBizOrderHandler());handlerMap.put(RightCenter, new RightCenterBizOrderHandler());handlerMap.put(PointDeduction, new PointDeductBizOrderHandler());}public static void main(String[] args) { // 这里可以从nacos配置中心读取责任链配置BizOrderHandler handler1 initHandler(Lists.newArrayList(Storage, RightCenter, PointDeduction,Payment));// 虚拟化一个产品订单ProductVO productVO ProductVO.builder().build();Result result handler1.handle(productVO);System.out.println(订单处理 成功);}/*** 根据责任链配置构建责任链* param handlerNameChain 责任链执行顺序* return 首个处理器*/private static BizOrderHandler initHandler(ListString handlerNameChain) {ListBizOrderHandler handlers new ArrayList();for (int i 0; i handlerNameChain.size(); i) {String cur handlerNameChain.get(i);String next i 1 handlerNameChain.size() ? handlerNameChain.get((i 1)) : null;BizOrderHandler handler handlerMap.get(cur);if (next ! null) {handler.setNextBizOrderHandler(handlerMap.get(next));}handlers.add(handler);}return handlers.get(0);}}上面的代码中通过initHandler这个方法来组建整个责任链条还能实现动态配置比如后期需要撤掉积分模块的商品处理改个配置就行感觉责任链完美搞定了这个问题第一版就这样开心上线。 4、第二次迭代
产品又来了提了一个新的需求 产品说我们需要支持多租户每种租户的订单流程都是不一样的 租户A仓储检查-权益扣减-积分扣减-剩余金额支付 租户B仓储检查-积分扣减-权益扣减
也就是说现在流程变成这样 来了多租户这有何难再加一条责任链配置不就好了直接写代码如下
public class OrderHandleCases {static MapString, BizOrderHandler handlerMap new HashMap();static {handlerMap.put(Storage, new StorageCheckBizOrderHandler());handlerMap.put(Payment, new PaymentBizOrderHandler());handlerMap.put(RightCenter, new RightCenterBizOrderHandler());handlerMap.put(PointDeduction, new PointDeductBizOrderHandler());}public static void main(String[] args) {// 租户A的责任链BizOrderHandler handler1 initHandler(Lists.newArrayList(Storage, RightCenter, PointDeduction, Payment));ProductVO productVO ProductVO.builder().build();Result result1 handler1.handle(productVO);// 租户B的责任链BizOrderHandler handler2 initHandler(Lists.newArrayList(Storage, PointDeduction, RightCenter));Result result2 handler2.handle(productVO);System.out.println(订单处理 成功);}/*** 根据责任链配置构建责任链* param handlerNameChain 责任链执行顺序* return 首个处理器*/private static BizOrderHandler initHandler(ListString handlerNameChain) {ListBizOrderHandler handlers new ArrayList();for (int i 0; i handlerNameChain.size(); i) {String cur handlerNameChain.get(i);String next i 1 handlerNameChain.size() ? handlerNameChain.get((i 1)) : null;BizOrderHandler handler handlerMap.get(cur);if (next ! null) {handler.setNextBizOrderHandler(handlerMap.get(next));}handlers.add(handler);}return handlers.get(0);}}上面的代码相比之前的就是多加了一条新的租户B的责任链配置。感觉这个功能不就实现了嘛 结果一运行堆栈溢出 咋回事 怎么堆栈溢出了咱们仔细看一下 发现咱们的Map里面存放的实例全部是单例搞出来了环形链表了.... 上图中黑色箭头代表第一个租户的流程绿色箭头代表第二个租户第二个租户的流程在执行到权益扣减环节后面由于第一个租户配置的下一个环节是积分扣减于是在这里形成了环。
看来单例不行咱们得搞多例 既然需要多次构建对象于是咱们搬出来下一个设计模式“简单工厂模式”
public class BizOrderHandlerFactory {public static BizOrderHandler buildBizOrderHandler(String bizType) {switch (bizType) {case Storage:return new StorageCheckBizOrderHandler();case Payment:return new PaymentBizOrderHandler();case RightCenter:return new RightCenterBizOrderHandler();case PointDeduction:return new PointDeductBizOrderHandler();default:return null;}}}然后我们改写initHandler方法不再从map中取实例转为从工厂方法里面获取实例
private static BizOrderHandler initHandlerPro(ListString handlerNameChain) {ListBizOrderHandler handlers new ArrayList();for (String s : handlerNameChain) {BizOrderHandler handler BizOrderHandlerFactory.buildBizOrderHandler(s);handlers.add(handler);}for (int i 0; i handlers.size(); i) {BizOrderHandler handler handlers.get(i);BizOrderHandler nextHandler i 1 handlerNameChain.size() ? handlers.get(i 1) : null;handler.setNextBizOrderHandler(nextHandler);}return handlers.get(0);
}public static void main(String[] args) {BizOrderHandler handler1 initHandlerPro(Lists.newArrayList(Storage, Payment, RightCenter, PointDeduction));BizOrderHandler handler2 initHandlerPro(Lists.newArrayList(Storage, RightCenter, PointDeduction));ProductVO productVO ProductVO.builder().build();Result result handler1.handle(productVO);System.out.println(订单处理 成功--租户1);result handler2.handle(productVO);System.out.println(订单处理 成功--租户2);
}执行代码 好了问题完美解决现在多租户也支持了。上线搞定这次需求
5、第三次迭代
产品又又来了提了一个新的需求
“ 产品说我们需要支持条件判断租户A要求权益扣减和积分扣减必须全部成功完成一个就可以进入支付环节不必都要把权益扣减和积分扣减流程走一遍 分析一下这个需求权益扣减和积分扣减都要完成才可以进入支付环节 当然最简单的改法是在权益和积分环节做个判断要是失败了就跳出责任链但是假如产品经理下次又说权益扣减和积分扣减完成一个就能进入支付我们还得修改这个权益和积分实现里面的判断频繁修改实现可并不是好事。
那咱们可以考虑代理模式熟悉网关的都知道网关其实就是一个大代理咱们按照这种思想可以搞一个网关代理权益扣减和积分扣减环节。于是咱们搞出来一个“网关”组件
Data
public class BizOrderHandlerUnionGateway extends BizOrderHandler {ListBizOrderHandler proxyHandlers;Overridepublic Result handle(ProductVO param) {boolean isAllSuccess true;if (proxyHandlers ! null) {for (BizOrderHandler handler : proxyHandlers) {Result result handler.handle(param);if (result.isSuccess()) {// 一个代理执行器 执行成功 则继续执行continue;} else {isAllSuccess false;break;}}}if (isAllSuccess) {return super.next(param);}else{throw new RuntimeException(execute Failed);}}
}上面的网关叫做union网关也就是并集网关也就是说代理的处理器全部都执行成功才继续传递责任链需要注意的是这个类也是BizOrderHandler的一个实现只不过它的内部没有逻辑只是对proxyHandlers中的组件进行代理。
然后简单修改下工厂 加一个分支
public static BizOrderHandler buildBizOrderHandler(String bizType) {switch (bizType) {case Storage:return new StorageCheckBizOrderHandler();case Payment:return new PaymentBizOrderHandler();case RightCenter:return new RightCenterBizOrderHandler();case PointDeduction:return new PointDeductBizOrderHandler();case UnionGateway:return new BizOrderHandlerUnionGateway();default:return null;}
}然后我们用下面的方法获取首个执行节点就可以执行整个责任链了
private static BizOrderHandler initHandlerWithGateway() {BizOrderHandler storage BizOrderHandlerFactory.buildBizOrderHandler(Storage);BizOrderHandler payment BizOrderHandlerFactory.buildBizOrderHandler(Payment);BizOrderHandler rightCenter BizOrderHandlerFactory.buildBizOrderHandler(RightCenter);BizOrderHandler pointDeduction BizOrderHandlerFactory.buildBizOrderHandler(PointDeduction);BizOrderHandlerUnionGateway unionGateway (BizOrderHandlerUnionGateway) BizOrderHandlerFactory.buildBizOrderHandler(UnionGateway);storage.setNextBizOrderHandler(unionGateway);unionGateway.setNextBizOrderHandler(payment);// unionGateway 加入责任链权益和积分交给这个uniongateway进行代理控制unionGateway.setProxyHandlers(Lists.newArrayList(rightCenter, pointDeduction));return storage;
}6、第四次迭代
产品又又又来了这次提了一个技术需求
“ 用户反馈生产订单流接口响应过慢页面卡顿观察接口发现目前的订单流程需要走的链路比较冗长虽然用了责任链模式但本质上代码执行仍然是同步的导致一个订单流完成耗费的时间过长现在希望订单流接口异步化然后需要发挥分布式部署的优势每一个环节可以单独分散到每个单个部署节点上执行。 这次我们发现问题需要异步化还要分布式这怎么办显然简单的内存责任链不行了咱们得上升到分布式责任链模式的方式那怎么实现分布式责任链呢咱们可以借助MQ来实现消息触发于是观察者模式上线这次咱们借助观察者模式的思想彻底完成分布式重构。
“ ps果然需求演进的最后就是重构不重构没有KPI。 咱们首先定义一个事件这个就是订单流事件
Data
public class OrderFlowEvent implements Serializable {private String orderNo;private String currentFlow;private String nextFlow;}这个事件可以在订单流发起的时候丢到消息队列里面然后就可以进行订单流的流转了下面我们来看消息处理逻辑咱们使用模板方法再次进行一次代码优化这里还是一个抽象类然后我们的支付、权益、积分只需要实现这个抽象类实现handleEvent逻辑就可以了此外我们只用一个Topic当前环节处理完成之后如果还有后续流程则再次发送消息到消息队列进行下一步处理此外handlerMap 代表责任链名称和责任链处理器的对应关系handlerChain则是责任链的环节配置。
Data
public abstract class BizHandler {String topicName biz_handle_topic;MapString, BizHandler handlerMap new HashMap();MapString, String handlerChain new LinkedHashMap();/*** 模板方法在收到订单流的消息之后将进到这里进行业务逻辑处理** param msg 订单流消息*/public void handle(String msg) {if (CollectionUtils.isEmpty(handlerMap) || CollectionUtils.isEmpty(handlerChain)) {//log.warn(handlerMap or handlerChain is empty);return;}OrderFlowEvent orderFlowEvent JSON.parseObject(msg, OrderFlowEvent.class);String currentFlow orderFlowEvent.getCurrentFlow();String nextFlow handlerChain.get(currentFlow);// 当前环节的处理器进行业务处理Result result handlerMap.get(currentFlow).handleEvent(orderFlowEvent);if (!result.isSuccess()) {throw new RuntimeException(handleException);}if (nextFlow null) {return;}if (result.isSuccess()) {// 处理成功并且还有后续流程则再次向订单流Topic中发送消息sendFlowMsg(result.getData(), nextFlow, handlerChain.get(nextFlow));}}public abstract Result handleEvent(OrderFlowEvent orderFlowEvent);public void sendFlowMsg(Object data, String currentFlow, String nextFlow) {OrderFlowEvent orderFlowEvent new OrderFlowEvent();orderFlowEvent.setCurrentFlow(currentFlow);orderFlowEvent.setNextFlow(nextFlow);MqUtils.sendMsg(topicName, JSON.toJSONString(orderFlowEvent));}}例如仓储环节可以这样实现
public class StorageBizHandler extends BizHandler {Overridepublic Result handleEvent(OrderFlowEvent orderFlowEvent) {System.out.println(StorageBizHandler handle orderFlowEvent JSON.toJSONString(orderFlowEvent));return Result.success();}
}使用的时候则可以这样
public class OrderFlowClient {void handleOrder() {// 定义一下流程名称和流程实例的对应关系MapString, BizHandler handlerMap new HashMap();handlerMap.put(Storage, new StorageBizHandler());handlerMap.put(PointDeduction, new PointDeductionBizHandler());handlerMap.put(Payment, new PaymentBizHandler());//注意这里用LinkedHashMap 保证顺序 key标识当前流程 value标识下一个流程MapString, String handlerChain new LinkedHashMap();handlerChain.put(Storage, PointDeduction);handlerChain.put(PointDeduction, Payment);handlerChain.put(Payment, null);// 开启分布式订单流转Map.EntryString, String first handlerChain.entrySet().iterator().next();String key first.getKey();OrderFlowEvent orderFlowEvent new OrderFlowEvent();orderFlowEvent.setCurrentFlow(Storage);orderFlowEvent.setOrderNo(order001123124123);handlerMap.get(key).handle(JSON.toJSONString(orderFlowEvent));}}最后咱们完成了这次大的技术演进需求但是就到此结束了吗按照这种设计思路改动之后你发现分布式环境下各种并发问题又出现了于是你还需要分布式锁来控制有了分布式锁你发现环节失败了还得引入重试逻辑重试应该怎么设计所以发现到了分布式系统下问题变得复杂了还得继续想办法一个个攻克。
6、总结
本文通过一次简单的需求演进分别讲述了责任链、模板方法、策略模式、工厂模式、代理模式、观察者模式的使用通过实际场景介绍下不同需求下如何通过适合的设计模式来解决问题。
最后说一句(求关注!别白嫖)
如果这篇文章对您有所帮助或者有所启发的话求一键三连点赞、转发、在看。
关注公众号woniuxgg在公众号中回复笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记回复面试、开发手册、有超赞的粉丝福利