设计模式复盘

一、背景

在项目中,对于单据的扩展是基于类似于接口扩展实现的。从业务横行来看,业务有A、B、C;从纵向来看,单个业务逻辑编排也可以划分为基础数据查询,决策判断,逻辑执行三大块。

单据扩展:平台构建单据基本信息,不同业务往单据中构建不同的信息。

二、抽象

将业务玩法+业务逻辑抽象可得策略模式和模版方法模式思维,可将二者通过Spring的加载机制链接在一起共同实现代码的高内聚,低耦合的特性。

三、线上运行版本

业务上下文

public class BusinessContext {private String type;private Map<String , String> features;}

1、抽象业务逻辑编排并且定位为接口,为策略模式提供基础

public interface BusinessStrategy {/*** desc:  查询节点:查询决策节点所需的数据*/Object query(BusinessContext businessContext);/*** desc:  决策节点:通过传入的业务上下文进行判断*/boolean isHandle(Object query);/*** desc: 业务逻辑具体执行节点*/void handle(BusinessContext businessContext ,Object query , Map<String, String> orderFeature);/*** desc: 策略管理类调用节点1*/default void execute(BusinessContext businessContext , Map<String, String> orderFeature) {Object query = query(businessContext);boolean handleFlag = isHandle(businessContext);if (handleFlag) {handle(businessContext, query , orderFeature);}}}

2、策略模式管理类和扩展入口

public class BusinessStrategyManager {@Resourceprivate List<BusinessStrategy> strategyList;public Map<String, String> doExecute(BusinessContext businessContext){Map<String, String> res = new HashMap<>();for (BusinessStrategy businessStrategy : strategyList) {//循环调用不同的实现businessStrategy.execute(businessContext , res);if (MapUtils.isNotEmpty(res)) {break;}}return res;}}

3、A业务实现(如果有其他实现,followA实现即可,不需要关心具体的bundle调用)

public class BusinessStrategyAImpl implements BusinessStrategy {@Overridepublic Object query(BusinessContext businessContext) {return "查询用来判断的数据";}/*** desc:  决策节点:通过传入的业务上下文进行判断** @param query*/@Overridepublic boolean isHandle(Object query) {System.out.println("判断的数据进行判断");return true;}/*** desc: 业务逻辑具体执行节点** @param businessContext* @param query* @param orderFeature*/@Overridepublic void handle(BusinessContext businessContext, Object query, Map<String, String> orderFeature) {System.out.println("业务逻辑执行");orderFeature.put("A" , "BusinessStrategyAImpl");}
}

四、运行版本中的问题

在策略模式管理类和扩展入口,最初的运行版本对于businessStrategy.execute()方法的异常是自己捕获的,并没有往外抛出,导致了单据未补充正确的信息,但是单据正常的创建了,以至于后续链路全部异常。

从业务视角下看,单据扩展信息补充发生异常时,应当阻断单据创建。从扩展框架的视角上看,实现类的异常应当直接抛出,不能被框架消化,否侧会导致使用者无法定位问题和发生预期之外的异常

五、复盘优化版本

该框架在线上运行过程中,虽然无异常问题,但是从代码层面来看,依然具有优化的空间。

通用的策略框架适用于调用方法清楚的知道需要调用那个策略类,调用方直接指定策略类,但是在当前扩展中,调用方也不知道具体调用策略类,需要业务实现类中查询数据之后再进行判断,对指定策略类的步骤进行了后置,导致通用策略类框架不适用于当前情况。

问题详情

如果有n个实现类,最差的情况需要把前面的n-1个实现类执行完成之后,才会执行到第n个实现类。

    • 前n-1个实现类中的基础数据查询阶段还不能出现异常(例如超时异常),否则都执行不到第n个实现类;
    • 前n-1个实现类中,基础数据查询到rpc调用耗时较久,性能较低

优化思路

  1. 将基础数据查询中的通用部分,例如orderA信息,orderB在策略模式管理类和扩展入口中先查询再透传到链路中,故不需要每一个实现类查询一次
  2. 再通过实现类+所需单据信息分为不同的组,可最大限度的减少rpc调用(可配置实现)

六、优化框架

 业务上下文

public class BusinessContext {private String type;private Map<String , String> features;}

1、抽象业务逻辑编排并且定位为接口,为策略模式提供基础

public interface BusinessStrategy2 {/*** desc: 决策节点*/boolean isHandle(BusinessContext businessContext);/*** desc: 业务逻辑具体执行节点*/void handle(BusinessContext businessContext, Map<String, String> orderFeature);/*** desc: 策略管理类调用节点2*/default void execute2(BusinessContext businessContext , Object query, Map<String, String> orderFeature) {boolean handleFlag = isHandle(businessContext);if (handleFlag) {handle(businessContext, orderFeature);}}}

2、策略模式管理类和扩展入口

public class BusinessStrategyManager2 {@Resourceprivate List<BusinessStrategy2> strategyList;private Map<String, List<BusinessStrategy2>> strategyMap = new HashMap<>();//配置项:配置业务实例和所需查询结果private Map<String, String> strategyGroupMap = new HashMap() {{put("A", "orderA");put("A1", "orderA");put("A2", "orderA");put("B", "orderB");put("B1", "orderB");put("B2", "orderB");}};//配置项:配置所需查询结果和查询对应的实例全类名private Map<String, String> queryGroupMap = new HashMap() {{put("orderA", "com.example.testproject.design.strategy.update.impl.QueryOrderAImpl");put("orderB", "com.example.testproject.design.strategy.update.impl.QueryOrderBImpl");}};@PostConstructpublic void buildStrategyMap() {//对业务实例根据所需查询结果进行分组for (BusinessStrategy2 strategy : strategyList) {if (strategyGroupMap.containsKey(strategy.getClass().getName())) {List<BusinessStrategy2> list = strategyMap.get(strategyGroupMap.get(strategy.getClass().getName()));if (Objects.isNull(list)) {list = new ArrayList<>();}list.add(strategy);strategyMap.put(strategyGroupMap.get(strategy.getClass().getName()), list);}}}public Map<String, String> doExecute(BusinessContext businessContext) {Map<String, String> res = new HashMap<>();for (Map.Entry<String, List<BusinessStrategy2>> stringListEntry : strategyMap.entrySet()) {//不同所需查询结果的组,获取不同的查询实例进行查询if (queryGroupMap.containsKey(stringListEntry.getKey())) {//获取配置的查询实例全类名String className = queryGroupMap.get(stringListEntry.getKey());//获取实例QueryOrderInter queryOrderInter = SpringUtils.getBean(className, QueryOrderInter.class);if (Objects.isNull(queryOrderInter)){throw new RuntimeException("未找到对应的查询实例,className={}" + className);}Object query = queryOrderInter.query(businessContext);List<BusinessStrategy2> value = stringListEntry.getValue();//所需查询结果的相同的组对查询结果进行消费for (BusinessStrategy2 strategy : value) {strategy.execute2(businessContext, query, res);if (MapUtils.isNotEmpty(res)) {break;}}}}return res;}}

3、查询抽象接口

public interface QueryOrderInter {Object query(BusinessContext businessContext);}

4、查询orderA

public class QueryOrderAImpl implements QueryOrderInter {@Overridepublic Object query(BusinessContext businessContext) {System.out.println("查询orderA");return "orderA";}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/628502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何区分GPT-3.5模型与GPT-4模型?

GPT 3.5 和 GPT-4 有什么区别&#xff1f; GPT-3.5 在经过大量数据训练后&#xff0c;成功地发展到可以考虑 1750 亿个参数以响应提示。这使其具备令人印象深刻的语言技能&#xff0c;以非常人性化的方式回应各种查询。然而&#xff0c;GPT-4 在更为庞大的训练数据基础上进行了…

腐烂的橘子 -- DFS、BFS

994. 腐烂的橘子 class OrangesRotting:"""994. 腐烂的橘子https://leetcode.cn/problems/rotting-oranges/description/"""def solution(self, grid: List[List[int]]) -> int:"""BFS时间复杂度 O(M*N)空间复杂度 O(M*N):par…

Vue keep-alive的使用和原理解析

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

linux建立基本网站

网站需求&#xff1a; 1.基于域名[www.openlab.com]可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[www.openlab.com/student] 网站访问学生信息 [www.openlab.com/data]网站访问教学资…

使用@JsonSerialize注解处理单位换算以及数据默认值

一、需求 使用JsonSerialize&#xff0c;来解决单位换算&#xff0c;以及数据为空时返回“**”默认值问题。例如有时候我们需要将金额&#xff0c;以万元的形式返回&#xff0c;需要千分位等等。 注意&#xff1a;如果使用fastjson进行序列化&#xff0c;则本注解不会生效。此…

2024年华为OD机试真题-转盘寿司-Java-OD统一考试(C卷)

题目描述: 寿司店周年庆,正在举办优惠活动回馈新老客户。 寿司转盘上总共有n盘寿司,prices[i]是第i盘寿司的价格,如果客户选择了第i盘寿司,寿司店免费赠送客户距离第i盘寿司最近的下一盘寿司 j,前提是prices[j]< prices[i],如果没有满足条件的 j,则不赠送寿司。 每个…

GPT获取session token

这里写自定义目录标题 概述通过浏览器获取session token通过ChatGPT网页获取session token 概述 ChatGPT是一种基于人工智能的对话模型&#xff0c;由OpenAI开发。通过使用ChatGPT的API&#xff0c;用户可以实现智能对话的功能。这篇文章将介绍如何获取ChatGPT Session Token以…

leetcode 67. 二进制求和

一、题目 二、解答 1.思路 1.1 思路1 转成2个二进制数字相加&#xff0c;之后再转回字符串 1.2 思路2 遍历字符串挨个相加&#xff1a; 补齐2个字符串到同样长度 while循环&#xff0c;如果指针>0不断循环如果a短&#xff0c;给字符串前插入&#xff08;a长度-b长度&a…

Java:多线程问题小结(二)

21、FutureTask是什么 这是一个比较偏实践的问题&#xff0c;这种问题我觉得挺有意义的。可以这么做&#xff1a; &#xff08;1&#xff09;获取项目的pid&#xff0c;jps或者ps -ef | grep java&#xff0c;这个前面有讲过 &#xff08;2&#xff09;top -H -p pid&#xff…

npm link 后怎么查看软连接和删除软连接的

一&#xff1a;在你的npm项目中&#xff0c;进行打包&#xff0c;形成一个dist文件 npm run build // 这是我的打包命令&#xff0c;具体可查看 package.json 文件 二&#xff1a; 打包完成后&#xff0c;运行pwd命令&#xff0c;可查看到你npm项目的路径。 pwd // 输出一…

Java开发笔记

一、参数校验 1、校验json字符串是否符合规范 &#xff08;1&#xff09;业务场景&#xff1a;接收前端传输过来的json串&#xff0c;需要将其写入数据库&#xff0c;写入之前需要校验其是否能够转换成对应实体类&#xff0c;以便后续从数据库读取   &#xff08;2&#xff0…

【Java 设计模式】创建型之工厂方法模式

文章目录 1. 定义2. 应用场景3. 代码实现4. 应用示例结语 在软件开发中&#xff0c;工厂方法模式是一种常见的创建型设计模式&#xff0c;它提供了一种将对象的实例化延迟到子类的方法。工厂方法模式通过定义一个创建对象的接口&#xff0c;但是让子类决定实例化哪个类。在本文…

C++ Primer 6.3 返回类型和return语句 知识点+练习题

C Primer 6.3 返回类型和return语句 无返回值函数有返回值的函数两个错误值是如何被返回的返回类类型的函数和调用运算符引用返回左值列表初始化返回值主函数main的返回值返回数组指针 递归练习题疑问待更新 无返回值函数 用在返回值类型为void的函数中&#xff0c;可以不写re…

若依基于jsencrypt实现前后端登录密码加密

若依虽然有加密解密功能&#xff0c;然后只有前端有&#xff0c;在用户点击保存密码的时候&#xff0c;会将密码保存到本地&#xff0c;但是为了防止密码泄露&#xff0c;所以在保存的时候&#xff0c;进行加密&#xff0c;在回显密码的时候进行解密显示&#xff0c;用户在登录…

29 旋转工具箱

效果演示 实现了一个菜单按钮的动画效果&#xff0c;当鼠标悬停在菜单按钮上时&#xff0c;菜单按钮会旋转315度&#xff0c;菜单按钮旋转的同时&#xff0c;菜单按钮旋转的8个小圆圈也会依次旋转360度&#xff0c;并且每个小圆圈的旋转方向和菜单按钮的旋转方向相反&#xff0…

数据结构期末复习(4)串 树和二叉树

串 在数据结构中&#xff0c;串是由零个或多个字符组成的有限序列。它是一种线性数据结构&#xff0c;常用于表示和处理文本、字符串等信息。 串的特点包括&#xff1a; 顺序性&#xff1a;串中的字符按照一定的先后顺序排列&#xff0c;每个字符都有一个唯一的位置。有限性&…

MATLAB - 利用非线性模型预测控制(Nonlinear MPC)来控制四旋翼飞行器

系列文章目录 前言 本示例展示了如何利用非线性模型预测控制&#xff08;MPC&#xff09;为四旋翼飞行器设计一个跟踪轨迹的控制器。 一、四旋翼模型 四旋翼飞行器有四个向上的旋翼。从四旋翼飞行器的质量中心出发&#xff0c;旋翼呈等距离的正方形排列。四旋翼飞行器动力学数…

记一次文件上传引发的OSS_ACCESS_KEY泄露

文章目录 一、漏洞原因二、漏洞成果三、漏洞利用0x01 任意用户注册0x02 文件上传处泄露OSS_AK0x03 OSS管理四、总结五、免责声明一、漏洞原因 由于是教育系统,可以进行任意用户注册;由于在身份认证的模块,可以进行文件上传,返回数据包中泄露OSS_ACCESS_KEY;通过图形化管理工…

【LeetCode】1. 两数之和(简单)——代码随想录算法训练营Day06

题目链接&#xff1a;1. 两数之和 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素…

uboot工作原理介绍

uboot其实和电脑的BIOS是一个原理&#xff0c;它主要做两件事: &#xff08;1&#xff09;初始化硬件&#xff1b; &#xff08;2&#xff09;将系统文件&#xff08;或者说是内核&#xff09;从flash中读出来加载到DDR里面执行。 给大家解释下面几个问题&#xff1a; 为什么…