公式系统
2020年4月16日
10:14
一,前言
几乎在所有的系统中,都有公式的影子,比如,表单上的计算项(通过公式计算该项的值),判断数据项是否显示的表达式,工作流中的路由表达式,计算审核人的条件表达式,薪酬系统中职工薪酬的计算公式等等,可以说公式无所不在。
二、公式系统需求
公式系统应该支持布尔表达式,字符串表达式,数学表达式。布尔表达式计算的结果是布尔值,字符串表达式的计算结果是字符串,数学表达式的计算结果是数值。
另外,还应该支持函数扩展、自定义变量,自定义运算符(这个比较难)。
自定义变量的意思是指,应该允许使用者定义有效的变量,解析公式时能识别该变量,计算时能得到变量的值。
自定义函数稍微复杂一些,使用者要把函数的签名,返回值,调用的方式告诉公式系统,以便公式系统能正确解析函数,并计算函数的值。
1、变量
变量是个三元组,定义如下:
变量:=(变量名称,变量类型[,变量值[,变量分类]])
公式通过变量名称来引用变量,变量数据类型公式编辑器进行数据类型检查,变量的值用于计算公式的值,在变量定义环节不使用。变量分类用于在公式定义界面中,按分类分级展示可用的变量。
2、变量数据类型
可以是简单类型,也可以是复杂的类型,比如xml对象,
公式系统支持简单的类型,也支持自定义类型。自定义类型就是java的类。
数据类型是个二元组,定义如下:
数据类型:=(类型名称,类型所在的程序集)。
所谓程序集时jar包(java),dll文件(.net)
3、自定义函数
自定义函数是个4元组,定义如下:
函数:=(函数名,返回值数据类型,参数列表[,函数分类])
参数列表:=(参数)|0~n
参数:=([参数名称,]数据类型)
三、公式上下文
上下文是指运行环境。运行为公式定义、解析和预算提供基础支持。
运行环境包括三个层级:
1、交易级别。比如在单据模型上定义数据项的计算公式,那么单据的数据项就是交易级别的变量。
2、系统级别。系统的一些全局变量,比如当前登录用户,登录的组织结构等。
3、企业级别。
公式系统要提供规范,支持这些自定义。包括在公式定义环节和公式解析计算环节。我们可以将变量,自定义函数,放到一个结构中传递给公式系统,我们称这个结构为公式运行上下文。
在公式定义和计算环节,首先要把运行上下文传给公式系统,公式系统基于上下文分析公式,计算公式的值。
公式上下文包括变量集合,数据类型集合,自定义函数集合。我们分别来讨论他。
1、变量集合
变量集合存放所有的自定义变量,公式系统从这个集合中检索变量,得到变量的数据类型和变量的值。
变量集合就是以上结构的列表,可以使用任何集合类型来实现,但是要方便根据变量名称检索变量。
2、数据类型集合
数据类型集合存放所有的自定义类型,公式系统从类型结合中检索类型,校验类型的合法性。
3、自定义函数集合
自定义函数集合存放所有的自定义函数,公式系统从该集合中检索函数,取得函数的定义元数据,以便检查公式定义的合法性,调用函数的计算方法,计算和看书的值。
四、公式系统的实现
公式系统应该包括几个大的模块:公式定义,公式解析,公式计算。
1,公式定义
公式系统应该提供一个界面,让用户来输入公式。输入公式可以有多种方式,直接输入,向导式输入,提供变量和函数的检索帮助功能。就是通常我们所说的“公式编辑器”。
公式定义的输入就是公式运行上下文,输出是编辑好的公式。
2、公式解析
公式解析有两个作用,一个是通过分析公式的结构,验证公式的合法性,并指出公式定义的错误。二是将公式翻译成形式化的结构,便于计算公式的结果。公式的形式化表示有很多种,比如前缀表达式,后缀表达式,中缀表达式等,这个可以参考编译原理。
3,公式计算
公式计算就是根据公式解析后的形式化表示,计算公式中变量的值,函数的值,最后得出公式最终的结果。
不同的表达式的结果类型是不同的,如逻辑表达式的结果就是布尔值,数学表达式的计算结果是数值。
公式计算的难点在函数的调用。函数的调用需要组装调用参数,实例化函数的实现类,通过反射的方式调用函数多对应的方法,得到函数的运算结果。
无论是公式定义,公式解析,还是公式计算,都离不开公式上下文,使用者负责组装公式上下文,传递给公式系统。
五、总结
公式系统的使用非常普遍,几乎无处不在。公式系统的核心是公式上下文和公式解析。一个设计良好的公式系统应该独立于任何外部使用者,通过公式上线文来适配不同的应用场景。