
2.1.3 状态机
状态机(State Machine)也是一种工作流类型,它相当于一个独立的模块,里面包含一个或多个实现了相对独立功能的Flowchart和Sequence。
State Machine中的一个重要概念是转换(Transition)。Transition由箭头或者状态之间的分支来表示,它可以添加从一种状态跳转到另一种状态的条件。因此某种程度上,State Machine可以看作是带有条件的Flowchart,适合实现复杂的企业化流程。
与Sequence和Flowchart不同,State Machine强调事件驱动和在不同状态之间自由流转。它总是停在一个预设的状态中,直到事件触发之后才会跳转到新的状态上,可前进到下一状态,可返回到上一状态,也可停留在当前活动内反复执行某一操作。
State Machine中存在唯一的开始节点,且只有两个Activity可以使用,分别是状态(State)和最终状态(Final State),状态间可以有一个或多个分支。在State Machine中添加状态,在不同的状态中放置不同的操作流程,并设置Transition中的触发器(Trigger)为下一个状态添加触发器。执行时一旦满足Trigger中的触发条件,State Machine就会执行下一个状态对应的操作。
图2-27是一个State Machine的项目主界面。在初始状态中会打开一个网址并进行登录,如果登录成功会进行数据处理,如果登录失败则会提示错误信息。在数据处理时,如果处理成功会进行数据记录,如果处理失败则会提示错误信息。大家可以先对这个案例有个简单的印象,接下来会详细介绍State Machine的每个部分,并配合实例来帮助理解。

图2-27 State Machine示例
State和Final State这两个活动都可以通过双击展开来查看更多信息并进行编辑。其中,State活动包括3个部分,即Entry(入口)、Exit(出口)和Transition(s)(转换),如图2-28所示。Entry和Exit用于为所选状态添加进入和退出状态时要执行的活动,而Transition(s)则显示连接到所选状态的所有转换。
当我们双击Transition时,Transition会被展开,就像State活动一样。它包含3个部分:Trigger(触发器)、Condition(条件)和Action(操作),用于为下一个状态添加触发器,或者添加要执行活动的条件,如图2-29所示。

图2-28 State示例

图2-29 Transition示例
而Final State活动中只有一个部分,即Entry,用于为所选状态添加进入状态时要执行的活动,如图2-30所示。

图2-30 Final State示例
在实际项目中使用State Machine时需要注意,官方要求只能创建一个初始状态,但可以有多个最终状态,我们可以通过在Transition中添加不同的条件来实现数据的传递以及自动化流程的切换。
我们以公司的采购流程为例:首先提交采购申请至项目审批,当采购金额小于等于1000美元时,如果项目审批通过则将成功信息录入系统等待采购,如果项目审批失败则将失败信息录入系统。当项目审批通过且付款金额大于1000美金时,需继续将采购申请提交至部门审批,如果部门审批通过则将成功信息录入系统等待采购,如果部门审批失败则将失败信息录入系统。这个需求对于不同的采购金额、审批状态会有不同的操作指示,下一个状态是相对不固定的,因此使用State Machine可以轻松实现。
【例2.3】使用State Machine完成猜测数字小游戏。我们会创建一个项目,它会自动生成一个1~100之间的随机数,并弹出窗口提示用户来猜测这个随机数。如果用户猜测的数字不正确,则提示用户猜大了或者是猜小了,请用户重新猜测;如果用户猜测的数字正确,则提示用户猜测正确并结束游戏。流程图如图2-31所示。

图2-31 流程图
具体实现步骤如下所示。
1)进入Studio界面,点击Process创建一个新流程,命名为2_3_State Machine,如图2-32所示。
2)点击DESIGN选项卡中的New按钮,选择State Machine工作流,如图2-33所示。
3)在弹出对话框中将新创建的State Machine工作流命名为StateMachine,而后点击Create按钮,如图2-34所示。

图2-32 新建流程

图2-33 新建State Machine

图2-34 命名创建的State Machine
4)在Variables面板中创建两个简单数字类型变量:GuessNumber和RandomNumber。第一个变量用于存储猜测的数字,第二个变量用于存储生成的随机数,如图2-35所示。

图2-35 创建变量
5)在Activities面板的搜索框内输入state,如图2-36所示。

图2-36 搜索State活动
6)将State活动拖入StateMachine状态机中,连接至Start节点,用于生成随机数字,如图2-37所示。

图2-37 拖入State活动
7)双击State活动使其展开,在Properties面板中,更改DisplayName属性为“生成随机数字”,如图2-38所示。

图2-38 展开State活动并更改属性
8)在Activities面板的搜索框内输入assign,如图2-39所示。

图2-39 搜索Assign活动
9)将Assign活动拖入State活动的Entry部分。在Properties面板中,在Assign活动的To属性中写入RandomNumber变量,在Value属性中写入new random().next(1,100),用于将生成的随机数存储在RandomNumber变量中,如图2-40所示。

图2-40 生成随机数字
10)回到主项目视图,再拖入一个State活动,将其连接到先前添加的活动,如图2-41所示。

图2-41 拖入State活动
11)双击State活动使其展开,在Properties面板中,更改DisplayName属性为“猜测数字”,如图2-42所示。

图2-42 展开State活动并更改属性
12)拖入一个Input Dialog活动到State活动的Entry部分。在Properties面板中,更改Label属性为“"请输入一个整数:"”,更改Title属性为“"输入数字"”,并在Result属性中写入GuessNumber变量,用于存储用户猜测的数字,如图2-43所示。

图2-43 拖入Input Dialog活动并更改属性
13)回到主项目视图,创建一个“猜测数字”状态指向自身的转换,如图2-44所示。
14)双击该转换使其展开,在Properties面板中,更改DisplayName属性为数字过小(此文字也会显示在主项目视图的箭头上),在Condition条件中写入GuessNumber<RandomNumber,用于判断猜测的数字是否比生成的随机数小,如图2-45所示。
15)在Action部分拖入一个Message Box活动,输入“"您猜测的数字过小,请重新猜测。"”,如图2-46所示。

图2-44 创建转换

图2-45 展开转换并更改属性

图2-46 猜测数字过小时弹出窗口设置
16)回到主项目视图,创建一个“猜测数字”状态指向自身的转换,如图2-47所示。

图2-47 创建转换
17)双击该转换使其展开,在Properties面板中,更改DisplayName属性为“数字过大”(此文字也会显示在主项目视图的箭头上),在Condition条件中写入GuessNumber>RandomNumber,用于判断猜测的数字是否比生成的随机数大,如图2-48所示。

图2-48 展开转换并更改属性
18)在Action部分拖入一个Message Box活动,输入“"您猜测的数字过大,请重新猜测。"”,如图2-49所示。

图2-49 猜测数字过大时弹出窗口设置

图2-50 搜索Final State活动
19)回到主项目视图,在Activities面板的搜索框内输入final state,如图2-50所示。注:State Machine中必须要有Final State活动,否则State Machine将无法结束。
20)将Final State活动拖入StateMachine中,创建一个“猜测数字”状态指向Final State的转换,如图2-51所示。

图2-51 拖入Final State活动并创建转换
21)双击该转换使其展开,在Properties面板中,更改DisplayName属性为“猜测正确”(此文字也会显示在主项目视图的箭头上),在Condition条件中写入GuessNumber=RandomNumber,用于判断猜测是数字是否等于生成的随机数,如图2-52所示。

图2-52 展开转换并更改属性
22)回到主项目视图,双击Final State使其展开,在Entry部分拖入一个Message Box活动,输入“"恭喜您,猜测正确!"”,如图2-53所示。

图2-53 猜测正确时弹出窗口设置
23)最终主项目视图如图2-54所示。
24)按F6键执行流程,系统将显示“输入数字”对话框,如图2-55所示。

图2-54 最终主项目视图
25)当猜测的数字过小时执行结果如图2-56所示,当猜测的数字过大时执行结果如图2-57所示,当猜测的数字正确时执行结果如图2-58所示。

图2-55 “输入数字”对话框

图2-56 数字过小时的执行结果

图2-57 数字过大时的执行结果

图2-58 数字正确时的执行结果