手寫工作流設計模式,針對常見的工作流步驟流轉,減少過多的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);}// 以此類推 ...}}
}

以上,就是今天的內容。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/165751.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165751.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165751.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

微信小程序實現類似Vue中的computed、watch功能

微信小程序實現類似Vue中的computed、watch功能 構建npm使用 構建npm 創建包管理器 進入小程序后&#xff0c;打開終端&#xff0c;點擊頂部“視圖” - “終端” 新建終端 使用 npm init -y初始化包管理器&#xff0c;生成一個package.json文件 安裝 npm 包 npm install --…

Java Web 實戰 21 - 用 Servlet 實現一個Hello World

用 Servlet 來寫一個 Hello World~ 一 . 基本部署方式1.1 創建 Servlet 項目1.2 引入依賴1.3 創建目錄1.4 編寫代碼繼承 HttpServlet重寫 doGet 方法刪除 super 方法加上 WebServlet 注解寫業務邏輯 1.5 打包1.6 部署1.7 驗證1.8 小結 二 . 更方便的部署方式2.1 Smart Tomcat 的…

【docker】安裝redis和mysql生產實戰

docker安裝諸如redis,mysql等程序非常方便,但是如果不是為了學習,生產環境的部署還是要注意很多問題的 mysql docker pull mysql:5.7mkdir -p /usr/docker/mysql/{conf,logs,data}docker run -d -p 3306:3306 --privilege

ORA-28003: password verification for the specified password failed,取消oracl密碼復雜度

自己在測試環境想要使自己的Oracle數據庫用戶使用簡單的密碼方便測試&#xff0c;結果指定密碼的密碼驗證失敗 SQL> alter user zzw identified by zzw; alter user zzw identified by zzw * ERROR at line 1: ORA-28003: password verification for the specified password…

本地部署 ComfyUI

本地部署 ComfyUI ComfyUI 介紹ComfyUI Github 地址部署 ComfyUI配置模型地址 or 下載模型啟動 ComfyUI訪問 ComfyUI ComfyUI 介紹 最強大、模塊化的穩定擴散 GUI 和后端。 該用戶界面將允許您使用基于圖形/節點/流程圖的界面設計和執行高級穩定擴散管道。 ComfyUI Github 地…

用戶運營常用的ChatGPT通用提示詞模板

如何建立和完善用戶運營體系&#xff0c;提高用戶滿意度和忠誠度&#xff1f; 如何制定有效的用戶獲取和留存策略&#xff0c;提高用戶生命周期價值&#xff1f; 如何運用多種渠道和平臺進行用戶運營&#xff0c;提高用戶參與度和互動性&#xff1f; 如何建立和維護用戶社群…

第五天 用Python批量處理Excel文件,實現自動化辦公

用Python批量處理Excel文件&#xff0c;實現自動化辦公 一、具體需求 有以下N個表&#xff0c;每個表的結構一樣&#xff0c;如下&#xff1a; 需要把所有表數據匯總&#xff0c;把每個人的得分、積分分別加起來&#xff0c;然后按總積分排名&#xff0c;總積分一致時&#xff…

小程序可拖拽按鈕

你有沒有遇到過在頁面中有一個固定在某個位置的按鈕&#xff0c;永遠的擋住了你想要看的區域&#xff1f; 在小程序的列表頁面中&#xff0c;常常會有一個提報的入口固定在右下角&#xff0c;如果這個按鈕不可拖動的話&#xff0c;可能會擋住下面的事件&#xff0c;讓用戶操作起…

云端導覽,數字互動 | 拓世法寶AI數字人一體機助力全新旅游時代

《中國旅行消費趨勢洞察白皮書&#xff08;2023版&#xff09;》顯示&#xff0c;消費者旅行習慣已從“到此一游”變為“深度在地”&#xff0c;更強調在旅游中充實自我、學習新知識。 &#xff08;《中國旅行消費趨勢洞察白皮書&#xff08;2023版》截圖&#xff09; 從這些資…

C++標準模板(STL)- 類型支持 (類型修改,從給定類型移除引用,std::remove_reference)

類型特性 類型特性定義一個編譯時基于模板的結構&#xff0c;以查詢或修改類型的屬性。 試圖特化定義于 <type_traits> 頭文件的模板導致未定義行為&#xff0c;除了 std::common_type 可依照其所描述特化。 定義于<type_traits>頭文件的模板可以用不完整類型實例…

Springboot整合MybatisPlus及分頁功能

1 引入pom <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot</artifactId><version>2.7.14</version> </dependency> <dependency><groupId>com.baomidou</groupId><a…

【Vue】Vue3 配置全局 scss 變量

variables.scss $color: #0c8ce9;vite.config.ts // 全局css變量css: {preprocessorOptions: {scss: {additionalData: import "/styles/variables.scss";,},},},.vue 文件使用

讀書筆記:彼得·德魯克《認識管理》第33章 管理者與管理科學

一、章節內容概述 把管理科學的潛力轉變為績效&#xff0c;主要取決于管理者。要做到這一點&#xff0c;管理者必須理解管理科學是什么以及能夠做什么。管理者必須明白&#xff0c;管理科學固有的特殊局限性在很大程度上源于自身的起源和歷史。但最重要的是&#xff0c;管理者…

Java大型電商項目——品優購(一)

視頻教程&#xff1a;【黑馬程序員】Java大型電商項目—品優購【配套源碼筆記】_嗶哩嗶哩_bilibili源碼下載&#xff1a; 鏈接&#xff1a;https://pan.baidu.com/s/1fECz5In_XCB-aW6ed6ZTbA 提取碼&#xff1a;27xa 技術選型&#xff1a; 后端框架&#xff1a;SpringSprin…

多功能回饋式交流電子負載的應用

多功能回饋式交流電子負載是用于模擬和測試電源、電池等電子設備的負載工具。它具有多種應用&#xff0c;可以用于測試和評估各種類型的電源&#xff0c;包括直流電源和交流電源。它可以模擬各種負載條件&#xff0c;如恒定電流、恒定電壓和恒定功率&#xff0c;以驗證電源的性…

小葉子鋼琴智能陪練 助力打牢鋼琴基礎

孩子在練琴過程中&#xff0c;經常會出現錯音錯節奏&#xff0c;為了能夠幫助她更高效的練琴&#xff0c;最近開始使用智能鋼琴陪練工具——小葉子鋼琴智能陪練。 身邊也有很多朋友在用這款應用&#xff0c;它比較知名的功能就是三大練琴模式&#xff0c;也就是識譜模式、提升…

linux centos系統命令安裝

Zip unzip 命令安裝下載 centos 命令常用常用下載 https://rpmfind.net/linux/rpm2html/search.php?queryzip%28x86-64%29&submitSearch…&system&arch 在線安裝zip命令 Centos用yum安裝的話用下面的命令安裝 yum install -y unzip zipUbuntu的的系統可以用下…

java SpringCloud版本b2b2c鴻鵠云商平臺全套解決方案 小程序商城免費搭建

使用技術&#xff1a; Spring CloudSpring BootMybatis微服務服務監控可視化運營 B2B2C平臺&#xff1a; 平臺管理端(包含自營) 商家平臺端(多商戶入駐) PC買家端、手機wap/公眾號買家端 微服務&#xff08;30個通用微服務如&#xff1a;商品、訂單、購物車、個人中心、支…

Ubuntu20.04清理垃圾vscode緩存

使用VM虛擬機安裝了Ubuntu系統&#xff0c;主目錄空間越來越小&#xff0c;硬盤擴容之后很快又空間不足&#xff0c;甚至出現了開機卡黑屏的情況&#xff0c;這里記錄一下解決過程。 1 重新開機進入系統 狀態&#xff1a;卡到了開機黑屏狀態&#xff0c;左上角有一條小橫杠 原…

國外網站文章或網頁采集翻譯為中文

采集國外網站的文章或網頁數據&#xff08;例如英文&#xff0c;西班牙語&#xff0c;法語等&#xff09;&#xff0c;怎么快速批量翻譯為中文&#xff1f; 可以使用簡數采集器來實現&#xff0c;支持自動翻譯&#xff0c;同時翻譯為多種語言&#xff08;不僅中文&#xff09;…