工作流与状态机

news2024/11/23 0:27:23

引言与动机:世界是那么的广阔无垠,姿态万千,我们梦想着计算设备的多元化,而如今我们已经梦想成真,但同时业务模型同样变得纷繁复杂。如果不考虑我们拥有的繁杂的业务模型,就很难谈得上去探索行业发展的方向。

MyWF 是基于有限状态机 FSM(Finite State Machine)理论的工作流引擎。状态机、状态机顾名思义着眼点在“状态(State)”上,以“状态”的不同为设计的参考物。一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。每请求一次,状态就更换一次,就执行对应的行为。State 模式采用了对这些不同的状态进行封装的方式处理这类问题,当状态改变的时候进行处理然后再切换到另一种状态,也就是说把状态的切换责任交给了具体的状态类去负责。

本文涉及的名词其关系如下图:

在这里插入图片描述
首选是 Workflow。Workflow 也可以说是案例(Case),相当于一次工作流的实作对象。例如处理案例,保险索赔、绩效考核、抵押申请等等都是案例。案例在出现和消失之间总是处于某个特定状态,这个状态有三个元素组成: 案例相关的属性,指出特定条件下案例是否被执行或者忽略;已经满足的条件,说明案例的进展;案例的内容,可能是文档、文件、档案或者数据库。

MyWF 把工作流视为一组关联的Steps,通过执行与 Step 相关的 Action,实现从一个 Step 到另一个 Step 的转换,从而引起工作流状态的流转 Transtion(又叫转换/转移)来推动工作的前进。换句话说,流转依赖于 Action 的发生。

<steps> 
    <step name="Step1" id="1"> 
        <actions> 
            <action name="Step1_Action" id="11"> 
                <results> 
                    <unconditional-result old-status="Finished" status="Waiting" step="2"/>
                </results> 
            </action> 
        </actions> 
    </step> 
    <step name="Step2" id="2"> 
        ...... 
    </step> 
<steps>

如上例,在 Step1 上执行了一个 Action,这个 Action 的结果是转向到 Step2。这样就达成了 Step1 到 Step2 之间的转换。

status 代表流程在某个 step中 的状态。譬如“待认领”、“审核不通过”之类的。对于 status 和 old-status 区别,就是前者表示转向到 Step2 后的状态值(Step2 的状态)下一个 Next 的状态;后者表示离开 Step1 后状态值(Step1 的状态)原来的状态。MyWF 的有 Underway(进行中)、Queued(等候处理中)、Finished(完成)三种 Status。一个实际 State(状态)真正是由两部分组成:State = (Step + Status) 。当转向的 Step 是类似于<step name="end" id="2" />时,则流程会自动终止。当转向的 Step 中包含的 Action 集合中存在一个 auto 属性为 true 的 Action 时,转向之后 Action 自动触发。如:

<step name="Step2" id="2"> 
    <actions> 
        <action name="Step2_Action" id="21" auto="true"> 
            <results> 
                <unconditional-result old-status="Finished" status="Ready1" split="100"/> 
            </results> 
        </action> 
    </actions> 
</step>

进入 Step2 后,Step2_Action 将自动执行。

如图一,除了上述的 Step,Action,Status,Result 几个基本的 Workflow 元素外还有两个特别的元素:分拆 Split 和合并 Join,用于描述并行工作的2个的元素。Split 提供多个 result;Join 则判断多个 current step 的状态,提供一个 Result。

到此小结一下:Step是一个工作流的逻辑单元。每个Step可以有多种状态(Status)和多个动作(Action),用 Workflow.getCurrentSteps()可以获得所有当前的Step,相当于流程所在的位置(注:如果有并列流程则可能同时有多个Step)。怎么设计 Step?Step 可以被定义为不能再细分的过程,即原子过程,这里存在主观因素。在定义或者分派 Step 的人看来,Step 是原子性的,但是对于执行它的人来说经常是非原子性(给程序封装了,执行它的人也就是用户只知道的是他的结果或应该下一步怎么做)。关于 Step 的颗粒度。

一个 Action 典型地由两部分组成:可以执行此动作的条件(conditions 可用脚本来判断),以及执行此动作的结果(results)。result 分为两种,conditional-result 和 unconditional-result。执行一个动作之后,首先判断所有 conditional-result 的条件是否满足,满足则使用该结果;如果没有任何 contidional-result 满足条件,则使用 unconditional-result。unconditional-result 需要指定两部分信息:old-status,表示“当前step的状态变成什么”;后续状态,可能是用 step+status 指定一个新状态,也可能进入 split 或者 join。

在这里插入图片描述

如何定义并行工作中的 Split 和 Join?在MyWF中与之对应的则是:<split><join>。从 Step2 分拆成2个并行任务(Step31,Step32),再合并并转向 Step4。可以如此理解:在 Step2 上执行一个 Action,其结果会导致 Step31 和 Step32 的产生;分别执行 Step31 和 Step32 的 Action,转向合并点;合并点判断 Step31 和 Step32 都完成后,那么转向 Step4。因此可描述为:

<steps> 
    ...... 

    <step name="Step2" id="2"> 

        <actions> 

            <action name="Step2_Action" id="21"> 

                <results> 

                    <unconditional-result old-status="Finished" status="Ready1" split="100"/> 

                </results> 

            </action> 

        </actions> 

    </step> 

    <step name="Split_Step" id="31"> 

        <actions> 

            <action name="Split_Step_Action" id="311"> 

                <results> 

                    <unconditional-result old-status="finished" status="Ready2" join="200"/> 

                </results> 

            </action> 

        </actions> 

    </step> 

    <step name="Split_Step" id="32"> 

        <actions> 

            <action name="Split_Step_Action" id="321"> 

                <results> 

                    <unconditional-result old-status="finished" status="Ready3" join="200"/> 

                </results> 

            </action> 

        </actions> 

    </step> 

    <step name="Join_Step" id="4"> 

        ...... 

    </step> 

</steps> 

<splits> 
    <split id="100"> 
        <unconditional-result old-status="finished" status="Ready5" step="31" /> 
        <unconditional-result old-status="finished" status="Ready6" step="32" />
    </split> 
</splits> 

<joins> 
    <join id="200"> 

        <conditions> 

            <condition type="beanshell"> 

                <arg name="script"> 

                    "finished".equals( jn.getStep(31).getStatus())&&"finished".equals( jn.getStep(32).getStatus()); 

                </arg> 

            </condition> 
        </conditions> 
        <unconditional-result old-status="finished" status="Ready7" step="4" /> 
    </join> 
</joins> 

这里<steps><splits><joins>可以理解为<step><split><join>对应的集合。关键是<result>(或<unconditional- result>),使得这些离散的建立了关联,确定了状态转换的目标。

何时该使用工作流的模式?

任何事物都有是否被适用的场景。我们应该尽可能划清楚边界,凸现某一样事物的作用性,发挥其长处、真正优秀的地方。工作流模式有许多种,可以很容易延伸到我们想到可以使用它的地方,连编程语言中也有 flow 流的概念,那这样的话,是不是就可以一概“套用”上去呢?

以我们一般的发布“博客”记录为例子,例如“POST New Entity-->表单验证-->写入数据库-->MVC耦合-->静态HTML”的例子。若非要采用工作流的概念去考量,其本质上是对工作流的路径建模,将路径信息都编码到了模型之中。当前的这种模式按工作流模式来说叫做“流模式”,“流模式”是工作流模式中的一种。实现上,就需要画出许多复杂的路径流程,但是这些复杂的路径却并非我们所关注的问题。明显,我们并不关心路径,而只关心结果,并且不关心结果是如何实现的。

状态机模式则不同。它并非完全是顺序执行的,可以“取回”,可以“退回”,执行的路径并非一成不变地走下去,可以接收外部事件或者启动并行任务。于是,各个点之间是可以跳跃的,令到参与者的自主性比效大,因此,使用工作流模式,一般的方案就集中在讨论状态机模式 State Machine 身上。

尽管我们谈到工作流模式的使用时机如何,但有时候,根据不同场景和角度切入,讨论哪一种模式不是并不是泾渭分明的。举一个例子,一个请假的工作流,参与都有两种:申请人、审批人:

  • 在申请人眼里,他所参与的工作流是[流模式]
  • 在审批人眼里,他所参与的工作流是[状态机模式]

虽然是同一种事情,结果也是不变的,但是设身处地地看,在不同参与者的眼中就有不同的模式。

工作流带来的好处

一个好的架构设计是一个项目快速成功完成的基础技术保证,没有这个技术基础,再先进的项目管理手段也是没有效率的,或者是笨拙的。

以一个游戏系统为例,在没有状态模式对状态提炼的情况下,状态改变由每个程序员想当然实现,为判断状态而产生的巨大的if或case语句,导致每个程序员开发的功能在整合时就无法开展。因为这个程序员可能不知道那个程序员的代码在什么运行条件下改变了游戏状态,结果导致自己的代码无法运行。可以说,最大的危险是系统没有一个一抓就灵的主体结构!这种现象实际上拒绝了项目管理的协作性,大大地拖延项目进度(程序员之间要反复商量讨论对方代码设计)。

因为,我需要做一个状态机 API,或者说状态机框架,供具体系统调用:类如公文流转应用或信息发报送应用等。将对象行为交给状态类维护后,对于上层程序而言,仅需要维护状态之间的转换规则从系统上的高度谈状态机状态模式,对于很多系统来说,它是架构组成一个重要部分。

参考

关于状态模式很经典的一篇文章《圣斗士星矢的状态模式和观察者模式》和设计模式参考《一句话总结GOF的23种设计模式》。

工作流可以用 XML 描述,也可以用脚本编码,这两者都是手工的编码,当然,如果更进一步发展的话,就是 GUI 图形协助工作流的编辑,甚至成熟的话,便是发展为IDE级别的。XML 模式的一个显著特点是,除了编写计算机语言之外,一个编辑的环境,最大的用途是用来交换数据、和数据扁平化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/828717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NSX多租户之旅

从多租户数据面到完整的多租户框架 我们很高兴地宣布NSX中的Projects这一项新功能&#xff0c;可以对NSX部署的多个租户进行细粒度的资源管理。 Projects提供灵活的资源分配和管理&#xff0c;将NSX的多租户支持提升到新的水平。企业管理员可以将平台划分为不同Projects&…

Java类集框架(一)

目录 1.Collection集合接口 2.List 接口 (常用子类 ArrayList ,LinkedList,Vector) 3.Set 集合 接口(常用子类 HashSet LinkedHashSet,TreeSet) 4.集合输出(iterator , Enumeration) 1.Collection集合接口 Collection是集合中最大父接口&#xff0c;在接口中定义了核心的…

SolidWorks二次开发系列入门100篇之97-极点坐标

什么是极点 一个模型中的极点是指在某个方向上的最高或最低点。在三维模型中&#xff0c;通常有三个方向&#xff1a;x轴、y轴和z轴。因此&#xff0c;在x轴&#xff0c;y轴和z轴的正方向和负方向上&#xff0c;每个模型可能都有两个极点。极点通常是一些锐角或骨刺&#xff0…

攻防世界zorropub题解与subprocess模块

目录 题目分析&#xff1a; subprocess模块&#xff1a; subprocess.Popen()函数&#xff1a; subprocess.run()函数&#xff1a; 题目脚本&#xff1a; 在攻防世界做到一个题目感觉还挺有意思&#xff0c;记录一下 这个放链接也只是攻防世界的页面&#xff0c;所以直接说…

docker数据持久化

在Docker中若要想实现容器数据的持久化&#xff08;所谓的数据持久化即数据不随着Container的结束而销毁&#xff09;&#xff0c;需要将数据从宿主机挂载到容器中。目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中。 &#xff08;1&#xff09;Volumes&#xff1a;…

【C#学习笔记】值类型(1)

虽然拥有编程基础的人可以很快地上手C#&#xff0c;但是依然需要学习C#的特性和基础。本系列是本人学习C#的笔记&#xff0c;完全按照微软官方文档编写&#xff0c;但是不适合没有编程基础的人。 文章目录 .NET 体系结构Hello&#xff0c;World类型和变量&#xff08;重要&…

分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离 (三)

本篇主要说明&#xff1a; 1. 因为这个mysql版本是8.0&#xff0c;所以当其中一台mysql节点挂掉之后&#xff0c;主从同步&#xff0c;甚至双向数据同步都失效了&#xff0c;所以本篇主要记录下当其中的节点挂掉之后如何再次生效。另外推荐大家使用mysql5.7的版本&#xff0c;这…

3-ASCII-座位渲染-二维码

一 ASCII码 1 概念 ascii码是一种计算机信息交换标准,在这个表里面制定了 128个数字跟128个字符的对应关系 我们只关注字母跟数字的对应关系 2 ASCII码转字符 let str String.fromCharCode(数字)二 js对象跟查询字符串互转 a js对象转查询字符串 //创建一个对象 let obj …

新SDK平台下载开源全志D1-H/D1s的SDK

获取SDK SDK 使用 Repo 工具管理&#xff0c;拉取 SDK 需要配置安装 Repo 工具。 Repo is a tool built on top of Git. Repo helps manage many Git repositories, does the uploads to revision control systems, and automates parts of the development workflow. Repo is…

【Python】模块学习之matplotlib柱状图、饼状图、动态图及解决中文显示问题

目录 前言 安装 pip安装 安装包安装 柱状图 主要方法 参数说明 示例代码 效果图 解决中文显示问题 修改后的图片 饼状图 主要方法 示例代码 效果图 动态图 主要方法 动态图官方使用介绍 示例代码 颜色设置 内建颜色 字体设置 资料获取方法 前言 众所周…

WPF上位机6——文件操作、多线程、线程锁、Task异步编程

文件操作 文件夹操作 创建文件夹 磁盘信息 文件的读写 文件流 Thread多线程 带参数创建线程 Task多线程 创建方式1 第一种 第二种 第三种&#xff1a;线程池的方式 前台与后台线程 线程锁 Task异步编程 task任务取消 task返回值 async await异步 并行库Parallel

CEC2014:CEC2014测试函数及多种智能优化算法求解CEC2014对比

目录 一、CEC2014测试函数 二、多种智能优化算法求解CEC2014 2.1 本文参与求解CEC2014的智能优化算法 2.2 部分测试函数运行结果与收敛曲线 三、曲线标记代码(获得代码后可自行更改&#xff09; 一、CEC2014测试函数 CEC2014测试集共有30个单目标测试函数&#xff0…

linux下docker安装、镜像下载、镜像基础操作、容器基础操作

目录 一、环境准备 1、开启虚拟化 2、关闭防火墙 3、yum仓库获取阿里源&#xff08;清华、京东都可以&#xff09; 4、确保能ping到外网 二、安装docker 1、yum安装docker 2、启动docker并设置开机自启 3、安装docker-ce阿里镜像加速器 三、docker基本操作 1、查看版…

如何在项目需求与技术方案未确定的情况下掌控上线时间?

需求不明确与技术方案未确定的挑战 在任何项目管理过程中&#xff0c;需求和技术方案是两个核心环节。理想情况下&#xff0c;我们希望在项目开始阶段就有清晰明确的需求和经过深思熟虑的技术方案。然而&#xff0c;现实中的项目管理往往并不如此理想。 项目需求的重要性 需求…

2023年全新版Java学习路线,精心整理【文中送书福利 】

小伙伴们大家好&#xff0c;这里是动力节点&#xff0c;我们从2009年开始一直在从事Java培训 到今年已经整14年了&#xff0c;虽然现在不缺培训机构&#xff0c;更不缺Java培训&#xff0c;但是像我们这么多年专注这一件事的应该也不多。我们只希望在“专业”两个字上面不断精…

Vue3 基础知识点汇总 自学笔记,记录难点 和 新知识点

1.vue3 基础 1.1vue3基础及创建 npm init vue@latest1.2.熟悉项目目录及关键文字 1.3 组合式API-setup 1.4.组合式 API reactive 和ref 函数 (都是为了生成响应式数据) 1.5.组合式API-computed 计算属性函数 1.6.watch 函数 1.7.组合式API-生命周期函数 1.8.组合式 API-父子…

Spring之事务实现方式及原理

目录 Spring事务简介 Spring支持事务管理的两种方式 编程式事务控制 声明式事务管理 Spring事务角色 未开启事务之前 开启Spring的事务管理后 事务配置 事务传播行为 事务传播行为的可选值 Spring事务简介 事务作用&#xff1a;在数据层保障一系列的数据库操作同成功…

Python之pyinstaller打包exe填坑总结

一、起因 编写了一个提取图片中文字的python脚本&#xff0c;想传给同事使用&#xff0c;但是同事电脑上没有任何python环境&#xff0c;更没有安装python库&#xff0c;因此想到通过pyinstaller打包成exe程序传给同事使用&#xff0c;于是开始了不断地挖坑填坑之旅 import p…

Kafka-消费者组消费流程

消费者向kafka集群发送消费请求&#xff0c;消费者客户端默认每次从kafka集群拉取50M数据&#xff0c;放到缓冲队列中&#xff0c;消费者从缓冲队列中每次拉取500条数据进行消费。

Dockerfile构建SSHD镜像

Dockerfile构建SSHD镜像 基于Dockerfile制作镜像时首先需要建立工作目录&#xff0c;作为生成镜像的工作目录&#xff0c;然后分别创建并编写 Dockerfile文件、需要运行的脚本文件以及要复制到容器中的文件。 1、环境配置&#xff1a; [rootdocker ~]# iptables -F [rootdoc…