手写工作流设计模式,针对常见的工作流步骤流转,减少过多的if/else,提升编程思维

需求

这一年下来,写两次工作流流转,总结下经验。
第一次写的时候,只找到用模版设计模式包裹一下,每个方法都做隔离,但是在具体分支实现的时候,if/else 满屏分,而且因为要针对不同情况,重复代码很多,但是if/else的条件又不一样,搞得我没办法用设计模式修改,想过用工厂模式重构。

一是没时间,二是工厂模式和策略模式基本上都用不来,
首先,工厂模式一定是if else分支较多,并且入参明确、固定。
策略模式也是不同的方法,实现不同的业务,入参明确、固定。
它们两者都不适合参数多一个少一个的情况,用起来只能说恶心自己。
并且由于设计模式的方法过多,时常debug需要嵌套跳转好几轮代码,就比较恶心。

这一年,闲下来我都会重构部分重复的代码,比如if else过多用设计模式优化,优化下来的感受是,没感觉可读性有多提高,反而感觉代码可读性变差了,有些案例的设计模式,很多情况没考虑到,比如较多重复代码,直接复用interface default里的方法,给我直观的体验是其他人来看这个代码,不太好理解。

还不如if else来的直接。

反正只要在if else上注释写清楚,管什么可读性。
在这里插入图片描述

设计模式做不到事

举个例子,当有个非常恶心的业务,需要在两层for循环里写if else,continue关键字是你贴心侍卫,常伴汝身。
你必须用continue它,艹,这个东西用设计模式就不合理,只能复用一些代码,放到一个方法里面去,什么两层for循环里,写个四五百行if else,调试都要好几天,我不知道要是业务出现变动,这个代码后面还怎么改。

新奇的思路

再次遇到工作流,吃过一次亏,不能走老路。
我选择网上冲浪,翻阅资料,最终找到一篇好用例子。
什么都没说,直接上项目,擦,一用才知道里面有坑。

原案例

  1. 定义流程节点
    首先定义一个抽象类ProcessNode,表示工作流中的一个节点:
public abstract class ProcessNode {
    private String nodeName; // 节点名称
    private List<ProcessNode> nextNodes; // 后继节点
    private boolean isEndNode; // 是否为结束节点
    public ProcessNode(String nodeName) {
        this.nodeName = nodeName;
        this.nextNodes = new ArrayList<>();
        this.isEndNode = false;
    }
    public String getNodeName() {
        return nodeName;
    }
    public void setNodeName(String nodeName) {
        this.nodeName = nodeName;
    }
    public List<ProcessNode> getNextNodes() {
        return nextNodes;
    }
    public void setNextNodes(List<ProcessNode> nextNodes) {
        this.nextNodes = nextNodes;
    }
    public boolean isEndNode() {
        return isEndNode;
    }
    public void setEndNode(boolean endNode) {
        isEndNode = endNode;
    }
}

其中,nodeName表示节点名称,nextNodes表示后继节点,isEndNode表示是否为结束节点。
接着,定义两个子类StartNode和EndNode,分别表示工作流的起始节点和结束节点:

public class StartNode extends ProcessNode {
    public StartNode() {
        super("Start");
    }
}
public class EndNode extends ProcessNode {
    public EndNode() {
        super("End");
        setEndNode(true);
    }
}
  1. 定义流程实例
    定义一个ProcessInstance类,表示一次工作流的执行实例:
public class ProcessInstance {
    private ProcessNode currentNode; // 当前节点
    public ProcessInstance(ProcessNode startNode) {
        this.currentNode = startNode;
    }
    public ProcessNode getCurrentNode() {
        return currentNode;
    }
    public void setCurrentNode(ProcessNode currentNode) {
        this.currentNode = currentNode;
    }
}

其中,currentNode表示当前执行到的节点。
执行工作流
定义一个ProcessEngine类,表示工作流引擎。该类包括以下方法:
addNodes:添加节点
run:执行工作流
代码如下:

public class ProcessEngine {
    private Map<String, ProcessNode> nodes; // 节点列表
    public ProcessEngine() {
        this.nodes = new HashMap<>();
    }
    /**
     * 添加节点
     */
    public void addNodes(ProcessNode... processNodes) {
        for (ProcessNode node : processNodes) {
            nodes.put(node.getNodeName(), node);
        }
    }
    /**
     * 执行工作流
     */
    public void run(ProcessInstance instance) {
        while (!instance.getCurrentNode().isEndNode()) {
            ProcessNode currentNode = instance.getCurrentNode();
            List<ProcessNode> nextNodes = currentNode.getNextNodes();
            if (nextNodes.isEmpty()) {
                throw new RuntimeException("No next node found.");
            } else if (nextNodes.size() == 1) {
                instance.setCurrentNode(nextNodes.get(0));
            } else {
                throw new RuntimeException("Multiple next nodes found.");
            }
        }
    }
}

测试
使用以下代码测试上述工作流引擎的功能:

public static void main(String[] args) {
    ProcessNode startNode = new StartNode();
    ProcessNode approveNode = new ProcessNode("Approve");
    ProcessNode endNode = new EndNode();
    startNode.setNextNodes(Arrays.asList(approveNode));
    approveNode.setNextNodes(Arrays.asList(endNode));
    ProcessEngine engine = new ProcessEngine();
    engine.addNodes(startNode, approveNode, endNode);
    ProcessInstance instance = new ProcessInstance(startNode);
    engine.run(instance);
    System.out.println("流程执行完成。");
}

运行结果为:
流程执行完成。

填坑

这个案例没考虑到每个Node都是一个function,它需要一个执行function,处理业务逻辑。
怎么玩呢?
使用Function<T, R>特性

public class EndNode extends ProcessNode {
    public EndNode() {
        super("End");
        setEndNode(true);
        System.out.println("执行end的任务");
    }

    public Object executeMethod(Integer languageId, Function<Integer, Object> function) {
        return function.apply(languageId);
    }
}

用这种方式把参数传递进去,并业务流转。
然后结合模版模式,把每个abstract的function看做Node,这样就能按照工作流一个方法执行完,执行下一个方法。

public abstract class TestTemplate {
	abstract Object handler(Integer languageId);

	// ...
}

第二点,这里缺少一个上一个方法流转结束,返回结果参数作为下一个方法的入参,这里没处理好,这样就会导致某个业务节点失败,回退到上一个节点,取不到入参的问题。

两种解决思路

第一种就是这些返回结果参数,一定要做数据库保存,到进入下一个节点,那这个流转入参就可以删除;

第二种
使用全局Map,并且不使用单例模式,bean注入,而是new Object。(这个不建议)

我们把ProcessEngine的Run方法改到template里面来,

public abstract class TestTemplate {
	abstract Object handler(Integer languageId);

	// ...

	public void init(Integer languageId) {
		ProcessNode startNode = new StartNode();
		ProcessNode endNode = new EndNode();
	    startNode.setNextNodes(Arrays.asList(endNode));
	    ProcessEngine engine = new ProcessEngine();
	    engine.addNodes(startNode, endNode);
	    ProcessInstance instance = new ProcessInstance(startNode);
    	run(languageId, instance);
	}
    /**
     * 执行工作流
     */
    private void run(Integer languageId, ProcessInstance instance) {
    	String queryJson = StringConstant.Symbol.BLANK;
        while (!instance.getCurrentNode().isEndNode()) {
            ProcessNode currentNode = instance.getCurrentNode();
            List<ProcessNode> nextNodes = currentNode.getNextNodes();
            if (nextNodes.isEmpty()) {
                throw new RuntimeException("No next node found.");
            }
            if (nextNodes.size() != 1) {
                throw new RuntimeException("Multiple next nodes found.");
            }
            instance.setCurrentNode(nextNodes.get(0));
            if (currentNode instanceof StartNode) {
            	StartNode startNode = (StartNode) currentNode;
            	Object obj = startNode.executeMethod(languageId, this::handler);
            	queryJson = JacksonUtil.writeJson(obj);
            } else if (currentNode instanceof EndNode) {
            	EndNode endNode = (EndNode) currentNode;
            	Object params = new Object();
            	if(StringUtils.isNotBlank(queryJson)) {
            		params = JacksonUtil.readJson(queryJson, Object.class);
            	} else {
            		// 从数据库读取上次function的结果参数
            	}
            	Object obj = endNode.executeMethod(params, this::handler);
            	queryJson = JacksonUtil.writeJson(obj);
			}
			// 以此类推 ...
        }
    }
}

以上,就是今天的内容。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过云服务器部署JavaWeb项目

文章目录 搭建Java运行环境部署项目更改部分项目代码打包项目把war包上传到webapps目录下验证程序 搭建Java运行环境 搭建环境的部分比较复杂&#xff0c;为了让大家的思路更加清晰特别总结为一篇博客点击查看 部署项目 更改部分项目代码 打包项目 把war包上传到webapps目录…

SpringBoot + 通义千问 + 自定义React组件,支持EventStream数据解析!

一、前言 大家好&#xff01;我是sum墨&#xff0c;一个一线的底层码农&#xff0c;平时喜欢研究和思考一些技术相关的问题并整理成文&#xff0c;限于本人水平&#xff0c;如果文章和代码有表述不当之处&#xff0c;还请不吝赐教。 最近ChatGPT非常受欢迎&#xff0c;尤其是…

链表的回文结构

题目描述 题目链接&#xff1a;链表的回文结构_牛客题霸_牛客网 (nowcoder.com) 题目分析 我们的思路是&#xff1a; 找到中间结点逆置后半段比对 我们可以简单画个图来表示一下&#xff1a; ‘ 奇数和偶数都是可以的 找中间结点 我们可以用快慢指针来找中&#xff1a;l…

Navicat 技术指引 | GaussDB服务器对象的创建/设计(编辑)

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

centos7中通过minikube安装Kubernetes

minikube是一款开源的Kubernetes集群管理器&#xff0c;它可以帮助您在本地计算机上轻松部署和管理Kubernetes集群。以下是minikube的安装和使用步骤&#xff1a; 安装Docker&#xff1a;如果您还没有安装Docker&#xff0c;可以从Docker官方网站上下载并安装适合您操作系统的…

2023年【四川省安全员B证】找解析及四川省安全员B证作业模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 四川省安全员B证找解析是安全生产模拟考试一点通总题库中生成的一套四川省安全员B证作业模拟考试&#xff0c;安全生产模拟考试一点通上四川省安全员B证作业手机同步练习。2023年【四川省安全员B证】找解析及四川省安…

大洋钻探系列之三IODP 342航次是干什么的?(下)

上文简要地介绍IODP342航次的总体情况&#xff0c;本文以航次1个钻孔&#xff08;U1403&#xff09;为例&#xff0c;更为详细地系统展示大洋钻探航次的工作和成果。 ​编辑​ 站位叠加多波束影像的成果图见下图&#xff0c;从图中的颜色效果可以看出&#xff0c;此多波束的成…

基于element自动表格

需求是根据JSON文件生成表格&#xff0c;包含配置和自动props属性以及过滤器&#xff1b; 数据示例&#xff1a; 表格设置&#xff1a; /*** 表格表头信息* chineseToPinYin: 这是封装的根据中文汉字转换为拼音的方法* prop: 表头字段名* filter: 数据过滤器* label: 表头显示…

归并排序算法

文章目录 归并排序一、归并排序思路二、归并排序算法模板三、题目代码 归并排序 一、归并排序思路 二、归并排序算法模板 void merge_sort(int q[], int l, int r) {if (l > r) return;int mid l r >> 1;//中间值merge_sort(q, l, mid);merge_sort(q, mid 1, r);…

「Verilog学习笔记」不重叠序列检测

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 题目要求检测a的序列&#xff0c;a为单bit输入&#xff0c;每个时刻可能具有不同的值&#xff0c; 当连续的六个输入值符合目标序列表示序列匹配&#xff0c;当六个输入值的…

基于命令行模式设计退款请求处理

前言 这篇文章的业务背景是基于我的另一篇文章: 对接苹果支付退款退单接口-CSDN博客 然后就是说设计模式是很开放的东西,可能我觉得合适,你可能觉得不合适,这里只是做下讨论,没有一定要各位同意的意思.... 相关图文件 这里我先把相关的图文件放上来,可能看着会比较清晰点 代码逻…

使用Python的turtle模块绘制钢铁侠图案

1.1引言&#xff1a; 在Python中&#xff0c;turtle模块是一个非常有趣且强大的工具&#xff0c;它允许我们以一个可视化和互动的方式学习编程。在本博客中&#xff0c;我们将使用turtle模块来绘制钢铁侠的图案。通过调用各种命令&#xff0c;我们可以引导turtle绘制出指定的图…

Unity UGUI的HorizontalLayoutGroup(水平布局)组件

Horizontal Layout Group | Unity UI | 1.0.0 1. 什么是HorizontalLayoutGroup组件&#xff1f; HorizontalLayoutGroup是Unity UGUI中的一种布局组件&#xff0c;用于在水平方向上对子物体进行排列和布局。它可以根据一定的规则自动调整子物体的位置和大小&#xff0c;使它…

详解深度学习中的图神经网络GNN

引言 图神经网络GNN是深度学习的一个分支。 深度学习的四个分支对应了四种常见的数据格式&#xff0c;前馈神经网络FNN处理表格数据&#xff0c;表格数据可以是特征向量&#xff0c;卷积神经网络CNN处理图像数据&#xff0c;循环神经网络RNN处理时序数据&#xff0c;图神经网…

NLP的使用

参考&#xff1a; Apache openNLP 简介 - 链滴 (ld246.com) opennlp 模型下载地址&#xff1a;Index of /apache/opennlp/models/ud-models-1.0/ (tencent.com) OpenNLP是一个流行的开源自然语言处理工具包&#xff0c;它提供了一系列的NLP模型和算法。然而&#xff0c;Open…

手写数字可视化_Python数据分析与可视化

手写数字可视化 手写数字流形学习 手写数字 手写数字无论是在数据可视化还是深度学习都是一个比较实用的案例。 数据在sklearn中&#xff0c;包含近2000份8 x 8的手写数字缩略图。 首先需要先下载数据&#xff0c;然后使用plt.imshow()对一些图形进行可视化&#xff1a; 打开c…

新材料制造ERP用哪个好?企业应当如何挑选适用的

有些新材料存在特殊性&#xff0c;并且在制造过程中对车间、设备、工艺、人员等方面提出更高的要求。还有些新材料加工流程复杂&#xff0c;涉及多种材料的请购、出入库、使用和管理等环节&#xff0c;解决各个业务环节无缝衔接问题是很多制造企业面临的管理难题。 新材料制造…

计算机网络——物理层相关习题(计算机专业考研全国统考历年真题)

目录 2012-34 原题 答案 解析 2018-34 原题 答案 解析 2009/2011-34 原题 答案 解析 2016-34 原题 答案 解析 2014-35/2017-34 原题 答案 解析 2013-34 原题 答案 解析 2015-34 原题 答案 解析 物理层的协议众多&#xff0c;这是因为物理层…

VSDX Annotator v1.16.1(Visio 绘图注释工具)

VSDX Annotator是一款在Mac上操作MSVisio绘图的工具&#xff0c;提供了广泛的注释可能性&#xff0c;以及在多平台环境中共享可视文档。它确保共有12个注释工具&#xff0c;并允许添加注释、标注、注释、块、图形文件等。该应用程序允许用户在Mac上查看Visio流程图、图表、方案…