前言
很多做业务开发的小伙伴或多或少的会遇到复杂的业务流程的流转
举个例子:
哇哦,看起来好乱哦~(无鼠标绘制,凑合看吧)
像类似这种的流程控制图,随着业务的不断迭代,细化分支不断地增加,我们的代码会逐渐变得繁琐且难以维护。
没错,我现在就面临这样的困境,随着客户间差异存在,流程中部分相同部分又有差异,想要复用逻辑又无从下手,没办法,只能另辟蹊径,推倒重来!
思考
1.流程拆解
要解决这种繁琐的流程控制及其分支,首先需要将每一个流程的分支单元独立,然后在进行自由组合,来应对不同客户间的差异以及最大程度的复用历史代码逻辑。
这里我先尝试对上述流程进行拆解,将每个子节点作为一个原子进行存储,然后交给有限状态自动机来进行调度,以此来实现一个简单的流程控制原型。
代码实现如下展示:
大家可以点击运行观察一下控制台的输入内容,是否基本符合上面流程图中的描述。
到这里,大家应该已经了解流程控制基本代码原理了,其实就是通过递归调用的方式,来完成每个流程分支的状态流转。
那么,新的问题就来了,上面的代码的确可以执行,但是,如果我希望可以有一个执行栈来管理这些原子流程节点,该怎么做呢?
2.流程管理
我们对代码进行小小的改造:
增加一个 actions 来管理原子事件,可以根据场景的不同任意调整待执行的任务。
栈有了,那么耦合在 Map 中的 resolve 该怎么处理呢?
我们需要使用一个指针 point,需要流程走下一步的时候,可以移动这根指针,来获取到下一节点的 action,当然不止这一种方法,大家可以集思广益。
代码实现如下展示:
3.参数传递
上面两步基本上满足了我们的事件原子化和流程编排控制,但是实际应用场景下,很可能存在业务参数传递的过程,那么我们如何将上下文 context 注入到整个流程中呢?
可以尝试利用闭包来动态生成整个 ProcessActionMap,通过一个 exec 函数收集参数的同时,启动整个流程装置。
由于闭包作用域的特殊性,可以让每一个 ProcessActionMap 中的 action 都可以访问到 context 上下文,这样就完成了参数的传递。
代码实现如下展示:
4.流程管理的进一步优化
上面代码中控制指针移动的方式,我们可以考虑封装为一个 next 函数,回到起点也封装为一个 retry 函数,通过函数参数的方式提供给 Action 使用,简化代码调用。
需要对 createMap 进行改造,除了定义 next、retry 函数之外,我们还需要将原本的事件 map 进一步独立出来,方便业务层面在单独的 js 中实现和改写,也为了方便后续我们封装抽象工具类。
代码实现如下展示:
5.封装抽象工具类
上面我们通过4个步骤进行拆解,逐步完善了我们的流程控制器,那么实际业务中肯定是不能写这么一大堆代码的,除了 ProcessActionMap 需要业务层面具体实现外,我们流程控制器可以抽象为一个独立的 class StatusMerchain 进行流程的生成和启动,降低业务层面使用难度。
代码整理后,如下展示:
总结
前端业务中很多流程控制逻辑,想要提高代码的重复利用率,就得考虑功能原子化与功能编排,那么就需要有相应的工具引擎来支持流程调度,这里浅浅和大家分享一下自己对这方面的思考,希望可以通过评论区交流找到更优的解决方案。