1. 单选分裂模式(排他选择模式)
原型实例(故事片段)
图 3.13 房改购房审批流程中的排他选择故事片段
如图 3.13 所示,“初审”环节之后,需要根据业务情况,选择“公告”或“复审”两个活动中的一个活动进行转出。例如,如果房改房的面积大于 70 平米就进行“公告”,否则直接提交给“复审”。
- 上下文(描述、动机)
描述:当前活动(初审)分裂为两个或多个后续分支,当前活动执行完毕后只能选择触发一个后续分支执行,即多选一。
动机:在现实生活或生产中,很多时候需要做出选择,就像你走到一个岔路口时,必须选择其中一条路走,排他选择模式就是提供了可以进行多选一的机制和功能的模式。还有一个更容易理解的例子,就是我们考试试卷中的单选题,只有一个答案是正确的,而在高级模式中的“多选模式”则对应试卷中的多选题。
- 问题的本质
排他选择的本质就是在多个可选的选择中,根据每个选择的执行条件输入当前的境况,并进行特定的匹配,当某个选择的执行条件与当前的境况一致时,就执行这个特定的选择。很多事情都有其执行的前提,就像我们出门,如果下雨天则带伞,如果晴天则不带。所以,是否下雨就是排他选择的判断标准。
- 解决方案及技术实现
解决方案。排他选择同样有两种解决方案:一种是在 XORSplit 节点上定义条件(如图 3.14 所示),另一种是在转移线上定义条件(图 3.15 所示)。
技术实现
(1) 定义期:在设计器中,为两个不同的方案提供了不同的定义界面。提供可求值的条件表达式(包括表达式中的变量)的输入及持久化存储机制。例如在本模式的故事中,首先定义一个工作流变量 int area,然后再定义个求值表达式:area>70。这样在运行期,area 的实际值(如房改房面积为 80 平米),与 area>70 这个表达式进行匹配,得出匹配结果为 true,所以需要进行“公告”。
(2) 运行期:在本模式的本质中我们讲到,排他选择的本质就是将当前境况与已经定义的条件进行匹配,根据匹配结果进行选择。因此,按照匹配的方式,本模式的实现可以分为人工匹配、基于数据的自动匹配两种场景。方案一和方案二只是在进行条件定义及匹配的位置上有所不同,本质的技术实现并没有不同,因此我们将只给出方案一的技术实现。
1. 人工匹配的技术实现:
顾名思义,人工匹配就是由人(活动 A 的执行人)在转出任务时,手动地选择“活动 B”或“活动 C”。因此,必须直接将活动 B 和活动 C 同时返回给活动 A 的办理人,由活动 A 的办理人选择是执行活动 B 还是活动 C;然后根据活动 A 的办理人所选择的活动,进行求值表达式的赋值。如果活动 A 的办理人选择了活动 B,则将“XORSplit 转到活动 B”这个转移线上的 condition 赋值为 1,即 set condition=1;否则将 condition 赋为任意不等于 1 的值,如 set condition=2。
需要说明的是,人工匹配本质上也是基于数据的匹配,因为人工选择之后还是要将选择结果所对应的数据赋值给求值表达式。在 BPMN 2.0 及 XPDL 2.1 规范中,排他选择模式的类型就是两种:基于数据的排他选择(Data-based Exclusive)和基于事件的排他选择(Event-based Exclusive)。在 BPMN 2.0 规范中,排他选择的类型默认为基于数据的。
2. 基于数据自动匹配的技术实现:
在本模式的故事片段中,房改房的面积在每次审批时都是不同的,对于受理人员来讲,他应该只负责填写房改房的面积等数据,至于提交之后是否要公示应该由流程自动判断,而不是由受理人员判断。此时,就需要引擎根据面积进行自动匹配。
1) 方案一的动态脚本语言的技术实现:
如果采用方案一,在定义期,在 XOR split 节点上,编写可进行求值的表达式,并持久保存。在运行期,调用动态脚本语言(BeanShell、Groovy 等)对此表达式进行求值,在本故事中,condition 为 area>70(也可以为 area<=70),客户端传入的值为 80,则调用 BeanShell 提供的 eval 求值方法对 condition 进行求值,详见 2.4.5 节中规则引擎的内容。
2) 方案一的规则引擎的技术实现:
在 XOR split 节点上定义规则库,由业务客户端传入事实库,然后由规则引擎进行规则匹配,详见 2.4.5 节中规则引擎的内容。
- 约束及可能存在的问题
问题:当采用工作流变量与表达式的技术实现本模式时,工作流变量如果定义为流程实例级别的,那么在驳回时就会遇到变量值是否要清空的问题。例如在上述流程中,如果“复审”不同意,将此审批流程驳回给了“受理”,此时,“受理”活动的受理人就可能要求购房申请人重新修改购房面积(例如改为 70 平米),这样工作流引擎在执行到 XORSplit 活动时继续取得工作流变量 area 的值。由于在第一次受理时,area 已经被赋值为 80,因此再次排他选择时,引擎依旧选择了“公共”活动。这与实际的需求就不一致了。
解决方案:
(1) 在驳回时或者 XORSplit 活动执行完毕后,清空工作流变量 area 的值(例如赋值为 -1;如果是 Integer 类型的,赋值为 null);
(2) 不要采用流程级别的实例变量,而要采用活动级别的变量。这样对于 area 变量,在活动被实例化时,就会生成一个不同的实例。
- 规范中的实现
XPDL 2.1**** 中的实现
<WorkflowProcess Id="3ef6aa14-5eb0-487d-9255-74ade5596129" Name="Process 1"> <Activities> <Activity Id="e13ce2f2-9038-4015-9e0d-ba35a6de3d4d" Name=" 活动 A"> <Description /> <Implementation> <Task /> </Implementation> </Activity> <Activity Id="07eac136-9d2d-449a-80a6-812f03e08d83" Name=" 排他选择分裂网关 "> <Description /> <Route MarkerVisible="true" /> </Activity> <Activity Id="700c6485-e12e-4f1d-8bbf-505893f8934a" Name=" 活动 B"> <Description /> <Implementation> <Task /> </Implementation> </Activity> <Activity Id="240f5cad-7550-4e3a-8d39-2e52a8985d9a" Name=" 活动 C"> <Description /> <Implementation> <Task /> </Implementation> </Activity> </Activities> <Transitions> <Transition Id="2a56817b-d40c-4a24-9acd-0111875083d7" From="e13ce2f2-9038-4015-9e0d-ba35a6de3d4d" To="07eac136-9d2d-449a-80a6-812f03e08d83" Name=""> <Condition /> </Transition> <Transition Id="21a54465-3563-4f8f-bf87-a81dfb700b45" From="07eac136-9d2d-449a-80a6-812f03e08d83" To="700c6485-e12e-4f1d-8bbf-505893f8934a" Name=""> <Condition Type="CONDITION"> <Expression>condition==1</Expression> </Condition> </Transition> <Transition Id="9822c8a4-b273-4451-b725-7ccd68755ad5"
如上所示,XPDL 2.1 规范中,采用的是方案二(参见图 3.15)实现的排他选择模式,即在转移线上设置触发条件。
BPEL 2.0**** 中的实现
排他选择模式与简单合并模式一般都是成对出现的,其在 BPEL 规范中的流程图如图 3.16 所示。
图 3.16 BPEL 规范中的排他选择模式与简单合并模式流程图
可以看出,在 BPEL2.0 规范中,对于排他选择模式,提供基于
<if standard-attributes> standard-elements <condition expressionLanguage="anyURI"?>bool-expr</condition> activity <elseif> <condition expressionLanguage="anyURI"?>bool-expr</condition> activity </elseif> <else> activity </else> </if>
基于以上结构,排他选择模式的具体 WSBPEL 2.0 定义实现如下:
<bpel:process name="exclusivePattern" targetNamespace="http://sample.bpel.org/bpel/sample" suppressJoinFailure="yes" xmlns:tns="http://sample.bpel.org/bpel/sample" xmlns:bpel="http://docs.oasis-open.org/wsbpel/2.0/process/executable" > <bpel:sequence name="main"> <bpel:receive name="receiveInput" partnerLink="client" portType="tns:switch" operation="initiate" variable="input" createInstance="yes"/> <bpel:empty name="Empty"></bpel:empty> <bpel:if name="days"> <bpel:condition expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[days<=3]]></bpel:condition> <bpel:invoke name="Invoke1"></bpel:invoke> <bpel:else> <bpel:invoke name="Invoke2"></bpel:invoke> </bpel:else> </bpel:if> <bpel:invoke name="Invoke"></bpel:invoke> <bpel:reply name="Reply"></bpel:reply> </bpel:sequence> </bpel:process>
我们注意到,在 WSBPEL 2.0 规范中,并没有显式地用来区分“分裂”与“汇聚”的网关,而是按照程序的逻辑默认执行,这也再次印证了 WSBPEL 2.0 是一种半结构化的语言。
BPMN 2.0**** 中的实现
<process id="sid-35e21b29-6800-47c6-b881-fee1c14917f5" isExecutable="false"> <task completionQuantity="1" id="sid-E5764C9A-E643-4752-AF7A-711A1C8A4542" isForCompensation="false" name=" 活动 A" startQuantity="1"> <outgoing>sid-700B2452-17F0-4779-84F2-F1B43E7D2A46</outgoing> </task> <exclusiveGateway gatewayDirection="Diverging" id="sid-CAB5BFB7-5767-41B0-86EB-9812E8881562" name=""> <incoming>sid-700B2452-17F0-4779-84F2-F1B43E7D2A46</incoming> <outgoing>sid-44AC65E4-E786-47EA-A528-057C593776DB</outgoing> <outgoing>sid-42231692-6651-4919-B8CD-62851B8F8E96</outgoing> </exclusiveGateway> <task completionQuantity="1" id="sid-1607CBCF-0DA9-45D8-9AE2-EE15C1A35218" isForCompensation="false" name=" 活动 B" startQuantity="1"> <incoming>sid-44AC65E4-E786-47EA-A528-057C593776DB</incoming> </task> <task completionQuantity="1" id="sid-D34AEF8E-3FC3-4361-B8BD-3B1F7114C744" isForCompensation="false" name=" 活动 C" startQuantity="1"> <incoming>sid-42231692-6651-4919-B8CD-62851B8F8E96</incoming> </task> <sequenceFlow id="sid-700B2452-17F0-4779-84F2-F1B43E7D2A46" name="" sourceRef="sid-E5764C9A-E643-4752-AF7A-711A1C8A4542" targetRef="sid-CAB5BFB7-5767-41B0-86EB-9812E8881562"/> <sequenceFlow id="sid-44AC65E4-E786-47EA-A528-057C593776DB" name=""
如上所示,在 BPMN 2.0 规范中采用的也是方案二(参见图 3.15)实现排他选择模式。
- 与其他模式的关系
(1) 与简单合并模式配对使用。
(2) 与基于事件的排他选择模式的区别:基于事件的排他选择模式是通过外部事件直接触发对路由选择的计算,而本模式是由人为触发对路由选择的计算的。
2. 单选汇聚模式(简单合并模式)
- 原型实例(故事片段)
图 3.17 房改购房审批流程中的简单合并故事片段
图 3.17 绘出了某个申请人的房改购房审批流程,在“初审”之后有可能需要经过“公告”再进入“复审”,也有可能直接进入“复审”。
- 上下文(描述、动机)
描述:在定义期,排他选择模式分裂出的两个或多个分支合并为一个后续分支。在实例期,对于同一个流程实例,只会有一个分支到达简单合并网关。这一点是通过排他选择模式的保证的。
动机:为排他选择模式提供一种简单的合并模式。
- 问题的本质
单选汇聚(简单合并)模式的本质,就是一次只允许一个分支通过。
- 解决方案及技术实现
解决方案。由于在此模式中不需要做任何的条件判断,因此解决方案同样也有两种:显式实现(如图 3.18 所示)及隐式实现(如图 3.19 所示)。
技术实现:
(1) 定义期:对于显式方案,直接在流程设计器中提供“简单合并网关”。对于隐式方案,则需要在“活动 D”上设置简单合并标志(或称之为“或汇聚”标志),并持久存储。
(2) 运行期:由于此模式与排他选择模式配对使用,而排他选择模式在运行期已经保证了 incoming 分支的唯一性,因此,这一模式在运行期不需要任何特定的动作。
- 约束及可能存在的问题
约束:该模式的约束是“简单合并网关”或者“活动 D”的进入分支(incoming Transition)有且只有一个会执行。其实,该约束已经被排他选择模式所保证了。如果出现了多个分支,就变成了另一个模式:多选汇聚模式。
- 规范中的实现
XPDL 2.1**** 中的实现
<WorkflowProcess Id="87a71a3c-2ce6-4309-a596-9f9b48bfa641" Name="Process 1"> <Activities> <Activity Id="a2f55f3d-b539-456c-a5da-1211ae3aebd3" Name=" 活动 C"> <Description /> <Implementation> <Task /> </Implementation> </Activity> <Activity Id="84633c75-7fdc-4069-ac64-351d1ddd2984" Name=" 活动 B"> <Description /> <Implementation> <Task /> </Implementation> </Activity> <Activity Id="9e2abbc2-c366-49c1-925b-a0f13270c88f" Name=" 简单合并网关 "> <Description /> <Route MarkerVisible="true" /> </Activity> <Activity Id="0a7d70c3-d92a-4271-87c1-fd7061783bd6" Name=" 活动 D"> <Description /> <Implementation> <Task /> </Implementation> </Activity> </Activities> <Transitions> <Transition Id="13fe4bcb-f081-4243-87d5-16becc28d918" From="84633c75-7fdc-4069-ac64-351d1ddd2984" To="9e2abbc2-c366-49c1-925b-a0f13270c88f"> <Condition /> </Transition> <Transition Id="5b212da5-0b0b-493b-a472-9b3d018c6d20" From="a2f55f3d-b539-456c-a5da-1211ae3aebd3" To="9e2abbc2-c366-49c1-925b-a0f13270c88f"> <Condition /> </Transition> <Transition Id="f867061f-54ca-4499-9ca9-9125e322592c" From="9e2abbc2-c366-49c1-925b-a0f13270c88f" To="0a7d70c3-d92a-4271-87c1-fd7061783bd6"> <Condition Type="CONDITION"> <Expression /> </Condition> </Transition> </Transitions> <ExtendedAttributes /> </WorkflowProcess>
在 XPDL 2.1 规范中,采用的是显式方案来实现简单合并模式。
BPEL 2.0**** 中的实现
在单选分裂模式中,我们讲到,WSBPEL 2.0 规范中不存在显式的“分裂”与“汇聚”网关,并且它是采用了程序语言的结构化实现方式(即直接用
BPMN 2.0**** 中实现
<process id="sid-16c272d4-2a98-48db-ae7b-4b6f54be6492" isExecutable="false"> <task completionQuantity="1" id="sid-A0DF4916-F0C0-4FF4-A767-E4F512E833D7" isForCompensation="false" name=" 活动 B" startQuantity="1"> <outgoing>sid-21F6C678-AA5F-4543-A5E2-5EDF2AADCC78</outgoing> </task> <exclusiveGateway gatewayDirection="Converging" id="sid-805F67E0-247F-4E96-AFDA-5264C21C585C" name=" 简单合并网关 "> <incoming>sid-2B37A9A9-2BC8-4515-A941-4D7AE0A869FE</incoming> <incoming>sid-21F6C678-AA5F-4543-A5E2-5EDF2AADCC78</incoming> <outgoing>sid-9E996A9E-D5B8-4D0C-8D33-EF03971EC7B4</outgoing> </exclusiveGateway> <task completionQuantity="1" id="sid-D800469F-638A-4255-A762-1D44E279901E" isForCompensation="false" name=" 活动 C" startQuantity="1"> <outgoing>sid-2B37A9A9-2BC8-4515-A941-4D7AE0A869FE</outgoing> </task> <task completionQuantity="1" id="sid-ED4A68C8-4FC0-405D-A114-EDE9A3D6BC1D" isForCompensation="false" name=" 活动 D" startQuantity="1"> <incoming>sid-9E996A9E-D5B8-4D0C-8D33-EF03971EC7B4</incoming> </task> <sequenceFlow id="sid-9E996A9E-D5B8-4D0C-8D33-EF03971EC7B4" name="" sourceRef="sid-805F67E0-247F-4E96-AFDA-5264C21C585C" targetRef="sid-ED4A68C8-4FC0-405D-A114-EDE9A3D6BC1D"/> <sequenceFlow id="sid-2B37A9A9-2BC8-4515-A941-4D7AE0A869FE" name="" sourceRef="sid-D800469F-638A-4255-A762-1D44E279901E" targetRef="sid-805F67E0-247F-4E96-AFDA-5264C21C585C"/> <sequenceFlow id="sid-21F6C678-AA5F-4543-A5E2-5EDF2AADCC78" name="" sourceRef="sid-A0DF4916-F0C0-4FF4-A767-E4F512E833D7" targetRef="sid-805F67E0-247F-4E96-AFDA-5264C21C585C"/> </process>
在 BPMN 2.0 规范中,同样也是采用显式方案来实现简单合并模式。需要注意的是,在 BPMN 2.0 规范中,排他网关与简单合并网关均采用 < exclusiveGateway >,只是通过 gatewayDirection 属性来区分是排他选择(gatewayDirection=“diverging”)还是简单合并(gatewayDirection=“converging”)。
- 与其他模式的关系
与排他选择模式配对使用。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论