使用职责链模式简化工作流相似步骤间的逻辑

简介:

在实际建立工作流模型时,往往会有很多相似步骤。相似步骤间的逻辑,往往又会包括大量的if..else..判断。

    例如,我们先看一个简单的报销流程。它很可能会是这样的:

 

image

 

     看似很简单的四个步骤。但客户会告诉我们: 部门经理应该分为两个步骤,因为有些部门设立了“副经理”职位,有些部门甚至“经理”职位空缺,只设“副经理”。换言之,“经理”与“副经理”至少有一个。

     于是我们将流程图改为以下情况:

image

 

     我们发现,增加了一个步骤后,需要增加四条“流程路径”。例如,按照途中的说明,当流程以及流转到了“部门副经理”时,取得下一个步骤需要做一次“if…else…”。如果当前部门有“部门经理”,则下一步流转到“部门经理”步骤;如果当前部门没有“部门经理”,则直接流转到“会计”步骤。

     这似乎能够满足客户的要求。但客户的组织机构往往非常复杂,他很可能在某一个时刻又告诉我们:我们共有两级部门,一共四个经理职位,均需要按照上述方式进行流转。

     这是非常清晰明了的一个需求。于是我们又继续修改流程模型为:

image

    此时我们就陷入了困境。这新增的两个步骤,带来了更多的逻辑关系,且每个逻辑关系都更为复杂。

    例如,假如当前流程以及流转到了“二级部门副经理”处,工作流平台取得下一步骤需要做的判断至少有:

  1. 如果该部门有“二级部门经理”,则流转到“二级部门经理”;
  2. 如果该部门没有“二级部门经理”,但有“一级部门副经理”,则流转到“一级部门副经理”;
  3. 如果该部门没有“二级部门经理”,也没有“一级部门副经理”,但有“一级部门经理”,则流转到“一级部门经理”
  4. 如果该部门没有“二级部门经理”,也没有“一级部门副经理”,也没有“一级部门经理”,则流转到“会计”步骤

     可以看到,仅仅“二级部门副经理”这一个步骤的“选择下一步骤”逻辑,就包含了4个“if…else…”判断,这是非常丑陋的。开发组往往兵强马壮,此种情况也可以通过大量的人工来保证。不过,可以预计的是,客户的多种流程(如报销、列账、冲销、预付等),都有类似的部门经理逻辑。大量的重复工作,无法保证不出错。

     更为恐怖的是,假如客户有一天,变成了三个层级的部门组织架构了呢? 从“三级部门副经理”开始,一直往上,到“会计”步骤,那是不是仍然采用这样简单粗暴的方式? 再按照这种方式来做的话,也许我们就直接崩溃了。

     怎么办呢?

     其实,在GoF的23个设计模式中,就已经提到了一个经典的解决方案:职责链模式

职责链(Chain of Responsibility)模式
    责任链模式是一种对象的行为模式【GOF95】。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

 

职责链模式的UML图:

_OEZ3HQO2}~EI1E94FEE1DT

 

这个UML图非常有意思。我们可以注意到,Handler角色自己聚合成自己。这有点类似于《数据结构》里描述的“链式结构”。

 

抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。

具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。

 

     我们将这个设计模式运用到刚才所提到的实际环境中。

     这时,我们的“抽象处理者”(Handler)是一个能够返回下一步步骤名称的处理类。对于上面提到的“二级部门”的情况,我们将会有五个“具体处理者”(ConcreteHandler):二级部门副经理、二级部门经理、一级部门副经理、一级部门经理、会计。 他们属于两个类别:“经理类”与“会计类”。

     “会计类”的处理无逻辑判断。即,当步骤选择器流入“会计类”时,直接返回“会计类”的步骤名。

     “经理类”就复杂一些,需要判断当前部门各个层级经理的有无、各个层级经理的是否已经审批。这两种判断可以抽象为两个数组参数的比较。我们设定两个数组参数byte[] candidates与byte[] approvers。第一个数组表示,候选步骤的有与无;第二个数字表示,在数据库记录中,所有步骤的当前审批状态.

     例如,假如一个部门有“二级部门副经理、二级部门经理、一级部门经理”,没有“一级部门副经理”,即所有职位从小到大排列,第三大的职位空缺,其余职位齐全——则byte[] candidates的值可以设为{1,1,0,1}。在流程刚刚开始时,所有步骤均未审批,byte[] approvers值为{0,0,0,0};假如当前流程已经流转到“二级部门副经理”步骤,则byte[] approvers可以的值为{1,0,0,0}。

     业务规则有了,参数有了,具体该如何处理呢?其实就是比较两个数组中相同index对应值的相等与否。

     例如,假设当前 candidates为{1,1,0,1},approvers为{1,0,0,0}。从数组索引0开始,依次往后,我们可以知道“需要”“二级部门副经理”审批,当前“二级部门副经理”已经审批;“需要”“二级部门经理”审批,当前“二级部门经理”还未审批。很明显,流程下一步应该流转到“二级部门经理”步骤。

     以上的自然语言描述翻译为算法为:按照索引从小到大的顺序,依次比较candidates数组与approvers数组的元素值是否相等;如果相等,跳到下一个索引处继续比较;如果不等,则返回当前索引所对照的步骤名称。

     按照我们设计好的“职责链模式”实现这个整体算法。即:每个“经理”处理者类,只判断属于自己索引的两数组元素,如果相等,则“处理”(返回步骤名);如果不等,则移交下家进行后续判断。

      首先构造处理者基类:

 

处理者基类

 

 

 

   构造两个类型的处理者子类:经理子类,会计子类。

 

具体处理者

 

    客户端:

客户端(示例)

 

    请注意“客户端”的代码,它完完全全地示例了职责链模式的调用方法。总体分为三步:1. 实例化处理者。2. 设置后继关系 3.从最前端的处理者开始处理。

    值得一提的是,这样的结构是符合“开放封闭原则”的(对修改封闭,对扩展开放)。也就是说,无论是需要两个部门经理来审批,还是四个部门经理来审批,甚至扩展到六个部门经理审批,都是不需要修改已有的业务逻辑代码,只需要在客户端中增加更多的处理者类实例、增加每个类的后继处理者、设置参数,就可以了。



本文转自 流牛木马 博客园博客,原文链接:http://www.cnblogs.com/azure/archive/2010/04/14/Chain_of_Responsibility_In_WorkflowPlatform.html,如需转载请自行联系原作者

相关文章
|
6月前
|
存储 缓存 监控
《优化接口设计的思路》系列:第二篇—接口用户上下文的设计与实现
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。
51 0
|
6月前
|
监控 小程序 Java
《优化接口设计的思路》系列:第五篇—接口发生异常如何统一处理
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
201 0
《优化接口设计的思路》系列:第五篇—接口发生异常如何统一处理
|
9月前
|
人工智能 数据可视化 前端开发
如何用smardaten无代码平台进行复杂逻辑编排?
如何用smardaten无代码平台进行复杂逻辑编排?
|
10月前
|
Java 测试技术 容器
工作中责任链模式用法及其使用场景?
工作中责任链模式用法及其使用场景?
81 0
|
10月前
|
Arthas JSON 前端开发
在接口自动化测试过程中,如何开展接口自动化测试?单个模块和多个模块关联又怎么去做测试?
在接口自动化测试过程中,如何开展接口自动化测试?单个模块和多个模块关联又怎么去做测试?
216 2
|
12月前
|
设计模式
简化理解:策略设计模式
本篇带来另外一种设计模式介绍,你或许天天和它打交道,但是不认识它,它就是“策略模式”。
简化理解:策略设计模式
|
存储 SQL 测试技术
手把手带你设计接口自动化测试用例(四):建立配置信息表,执行结果记录表...
手把手带你设计接口自动化测试用例(四):建立配置信息表,执行结果记录表...
116 0
手把手带你设计接口自动化测试用例(四):建立配置信息表,执行结果记录表...
|
存储 消息中间件 监控
复杂任务中,流程的解耦设计
在系统开发的过程中,必然存在耗时极高的动作,是基于请求响应模式无法解决的问题,通常会采用解耦的思维,并基于异步或者事件驱动的方式去调度整个流程的完整执行。
370 0
复杂任务中,流程的解耦设计
|
JSON 运维 监控
接口测试框架实战 | 流程封装与基于加密接口的测试用例设计
接口测试仅仅掌握 Requests 或者其他一些功能强大的库的用法,是远远不够的,还需要具备能根据公司的业务流程以及需求去定制化一个接口自动化测试框架的能力。所以,接下来,我们主要介绍下接口测试用例分析以及通用的流程封装是如何完成的。 首先在做用例分析之前,可以通过追查公司一年来所有的故障原因,定位问题起因,或者通过与 CTO、产品经理、研发、运维、测试调查,得到质量痛点,还可以分析业务架构、
|
JSON 运维 监控
接口测试框架实战 | 流程封装与基于加密接口的测试用例设计
接口测试框架实战 | 流程封装与基于加密接口的测试用例设计