4.规则集
4.规则集
概述
规则集也叫决策集,在URule Pro当中是由一组普通规则和循环规则构成的规则集合,是使用频率最高的一种业务规则实现方式。
在URule Pro中规则有两种类型:一种是普通规则;一种是循环规则。所谓的普通规则是指一种由如果、那么、否则三个部分构成的规则;而循环规则顾名思义就是可循环的规则,它允许指定一个集合类型的对象,对这个集合中每个对象进行循环迭代,在循环体中则是若干个由如果、那么、否则构成的普通规则。
在定义方式上,URule Pro提供了向导式规则集以及脚本式规则集定义两种。
所谓向导式规则集是指利用引擎提供的设计器,一步一步通过鼠标点击就可以完成其中的普通规则与循环规则的配置,配合高度可视化的向导式规则设计器,可以最大限度将业务规则可视化,降低规则配置的复杂度;而脚本式规则集顾名思义就是通过在规则集文件当中按URule Pro的脚本语法规范来书写脚本实现普通规则与循环规则的定义工作。
向导式规则集因为是图形化,向导方式构建规则,所以适合业务人员使用;而脚本式规则集通过书写脚本构成规则,与传统代码编写类似,所以适合技术人员来编写规则;从功能上看,向导式规则集和脚本式规则集能实现的功能是完全相同的,也就是说向导式规则集中能实现的功能在脚本式规则集也完全可以实现,反之亦然。
在URule Pro当中,虽然说脚本式规则能实现向导式规则中提供的所有功能,但我们还是推荐大家采用向导式规则集文件来定义我们的业务规则,原因很简单,向导式规则构建规则方式是可视化的,构建好的规则可读性更好,这样构建业务规则中出错的机率就会更小。
打开一个URule Pro的项目,在“决策集”节点右键选择创建一个向导式决策集文件,创建好的文件通过URule Pro向导式决策集设计器打开后的效果如下:
在向导式决策集的设计器中,通过顶部的工具栏,点击“添加规则”按钮可以添加一个普通的由如果、那么、否则构成的普通规则;点击“添加循环规则”按钮可以添加一个可以循环的规则。之前介绍的变量、常量、参数、动作四种类型的库文件,在向导式规则里就可以导入使用了。
普通规则
点击“添加规则”按钮就在下面的工作区里添加了一个普通的规则,如下图所示:
一个普通规则主体是由如果、那么、否则构成,点击规则名可以对规则名进行修改,修改完成后鼠标点击页签处离开焦点就完成了规则名的修改确认;点击“添加属性”链接可以为当前规则定义相关属性。无论是普通规则还是循环规则都支持下面这些属性。
中文属性名 | 英文属性名 | 值类型 | 描述 |
---|---|---|---|
优先级 | salience | 数字 | 当有多个规则满足条件时,这个值用来决定这些满足条件规则中动作的执行顺序,值越大,执行顺序越靠前。如不设置这个值,那按条件满足的顺序执行,也就是说如果不配置优先级属性规则的执行顺序是不确定的。 |
生效日期 | effective-date | 字符串 | 当规则设置了生效日期,表示这个规则只有在当前系统日期为大于等于生效日期时才会生效,否则即使条件满足也不会触发当前规则,如不设置,则不会对规则执行产生影响。该属性的值要求是一个日期格式的字符串,格式为:yyyy-MM-dd HH:mm:ss |
失效日期 | expires-date | 字符串 | 与生效日期对应,当规则设置失效日期时,一旦当前系统日期大于或等于失效日期,即使条件满足规则也不会触发执行,如不设置,则不会对规则执行产生影响。属性的值要求是一个日期格式的字符串,格式为:yyyy-MM-dd HH:mm:ss |
是否启用 | enabled | 布尔值 | 默认值为true,也就是启用当前规则;如设置为false,即使条件满足规则也不会触发执行,如不设置,则不会对规则执行产生影响。 |
允许调试信息输出 | debug | 布尔值 | 默认为false,表示不输出调试信息,设置为true后,规则在执行时会在控制台输出规则条件的匹配信息、规则动作的执行信息;如果规则计算过程出现异常,还会在控制台输出计算出现异常的位置,以便于我们快速定义规则错误位置。需要注意的是,如果项目的urule.debug属性设置为false时,规则的这个属性会被覆盖,也就是说即使设置为true也不会输出任何信息。 |
互斥组 | activation-group | 字符串 | 系统会自动将此属性相同的规则划为一组,且这个组中只有一个规则会执行,待执行的规则如设置了优先级,则优先级最高的规则执行,否则随机;需要注意的是,互斥组属性仅在当前规则集文件默认模式下有用,在顺序模式下互斥组属性将不起作用。 |
执行组 | agenda-group | 字符串 | 系统会自动将此属性相同的规则划为一组,默认情况下,引擎不会执行这个组里的规则,需要我们在定义规则动作时利用系统内置的函数显示的指定要激活执行的执行组名,这样系统才会尝试匹配并执行组里的规则。 |
允许循环触发 | loop | 布尔值 | 当执行“更新工作区对象”动作时,某些规则可以会再次满足条件,这时这个属性就是用来决定这种类型的规则是否允许再次触发执行。关于“更新工作区对象”请参考“更新工作区“章节介绍 |
从2.1.7版本开始,向导式规则文件工具栏上新增了一个用于控制当前文件中所有向导式规则日志输出的开关按钮,它的默认状态为“禁用调试日志输出”,如果我们希望打开当前文件中所有向导式规则的日志输出功能,那么只需要在这里
将“禁用调试日志输出”改为“允许调试日志输出”即可。
规则名及属性定义完成后,接下来就可以开始配置规则的主体部分,首先是“如果”部分,在如果部分当中可以添加若干条件,添加完条件后,就可以通过鼠标点击以向导方式设置条件,如下图:
对于一个具体的条件来说,我们可以将其分为三个部分,分别是条件左边部分、比较操作符以及条件右边部分。条件左边部分,如上图所示我们可选择的有变量、参数或者方法或函数,当然这相应的需要我们导入相关的变量库、参数库以及方法库;对于操作符目前URule Pro当中提供了下面这些操作比较符,如下图所示:
这些操作比较符基本已涵盖我们业务当中所有类型的比较操作。选择完比较操作符后,我们就可以来设置条件右边部分。条件右边部分相比左边,可选择的值类型会更多一些,如下图:
一旦选择某种类型值之后,我们就可以进入下一步操作,同时在URule Pro当中无论条件左值还是条件右值都可以进行无限多级简单的加、减、乘、除运算操作,如下图所示:
在配置条件时,需要我们把之前定义好的变量库文件、参数库文件、常量库文件、动作库文件导入进来。对于条件来说,可以是多个条件,也可以是多个复合条件组合,这样都是通过鼠标点击操作完成,这里就不再赘述。
条件配置完成后,可以为“那么”或“否则”部分添加动作,那么部分的动作只有条件满足时执行,反之,否则部分的动作只在条件不满足时执行,不加动作意味着什么也不干,动作可以有多个,多个动作添加完成后可以通过拖曳改变顺序。
目前在URule Pro当中支持的动作类型有三种,分别是:打印内容到控制台、变量赋值以及执行方法或函数,如下图所示:
所谓的”打印内容到控制台“其实就是将我们需要的信息打印输出到java 控制台,对于内容可以是一个普通的输入值,也可以是一些复杂的值类型或它们的加、减、乘、除组合,及添加括号定义算术运行优先级;变量赋值也就是给当前导入的变量库或参数库的值进行赋值,值类型可以是一个普通的输入值,也可以是一些复杂的值类型或它们的加、减、乘、除组合,及添加括号定义算术运行优先级;最后一种类型的动作是执行方法或函数,要选择执行的方法或函数,前提是我们必须方法所在的动作库文件导入到当前规则文件当中,否则就看不到要执行的方法,一旦选择执行方法后,如果当前方法当中包含参数,那么我们也需要选择相应的值为参数赋值,同样参数的值可以是一个普通的输入值,也可以是一些复杂的值类型或它们的加、减、乘、除组合。动作的具体配置都是基于鼠标点击操作的,比较简单,这里不再赘述。
在向导式规则编辑器中,可以通过鼠标拖曳来改变那么或否则部分的动作顺序,同时,对于多个规则文件,也可以通过拖曳来改变它们的显示顺序。
示例
下面是一个包含两个普通规则的决策集文件,导入了我们之前配置的那个包含Customer的变量库文件,在第一个规则当中条件满足时执行两个动作,不满足执行一个动作;第二个规则条件满足的话执行两个动作,不满足什么也不做,如下图所示:
可以看到这两个规则条件都比较简单,并且他们是互斥的,同一时刻最多只会满足一个规则。接下来需要对这个决策集进行测试,测试方法比较简单,点击项目的“知识包”节点,在打开的编辑器中添加一个知识包,并将这个做好的规则集文件放到这个包中,如下图所示:
知识包是URule Pro中提供的一种用于将一个或多个规则集、决策表、交叉决策表、决策树、评分卡、复杂评分卡、评分流文件打包的工具,知识包的编码属性比较重要,是这个包在当前项目中的ID。 定义好知识包及这个包中包含的资源文件后,可以点击工具栏上的“快速测试”按钮对当前知识包进行仿真测试。
点击工具栏上的“快速测试”按钮可以看到如下图所示的界面:
快速测试是URule Pro当中提供的一种针对知识包的测试工具,通过它可对定义好的知识包预先进行测试。快速测试中,会将这个知识包中涉及到的所有BOM对象罗列出来,用户可以对BOM对象的属性进行赋值测试。
因为我们当前知识包中只有一个决策集文件,而这个文件中只导入了我们之前定义的那个包含Customer实体对象的变量库文件,所以在上面的仿真测试页面中我们看到只有一个名为“会员”的BOM对象,在右边表格中罗列了会员的所有属性,我们可以在“值”列中对这些属性进行赋值,然后点击工具栏上的“测试决策包”就可以将当前BOM的值提交到引擎中进行测试,如下图所示。
上图中我们设置“年龄”属性为20,“是否有房”属性为true,“等级”属性为3,这样就满足第一个规则,所以在点击“测试决策包”按钮后,BOM的“名称”属性会被命名为“金牌会员”,控制台也会有相应输出,这里还有其它的一些情况,可以分别输入不同的值进行测试,这里就不再赘述。
在仿真测试窗口的工具栏中,“测试决策流”主要是针对决策流进行测试,如果我们的知识包中包含决策流,要对其进行测试,那么就点这个按钮实现;“查看规则树”按钮是用来通过图形化展示当前知识包所对应的规则树结构,如上面的示例,点示此按钮看到的规则树效果如下:
可以看到,在这棵树中,把规则定义用到的所有条件节点都以树节点的形式表现出来,条件之间如果是并且关系,那么这些条件节点在树中就以串行的形式连接,这样在实际执行时只有前一个节点条件满足才会流转到下一节点;条件之间如果是或者关系,可以看到规则树会通过一个"OR"节点将若干个条件节点连接,这样在实际执行时,只要有一边连线到达这个“OR”节点,那么与这个“OR”节点连接的其它连线上的条件节点就不再计算;如果总的条件使用并且连接,但分支上有并且和或者连接的条件,那么在构建规则树时就会将分支上的并且条件以串行的形式连接,同时将分支上的或者条件使用“OR”节点连接,最后再使用“AND”节点将所有条件连接起来,如上图所示。
如果规则中有两个相同的条件,那么在构建规则树时会智能地将这些相同节点合并为一个节点,这样在树上只会显示一个节点,通过这种相同条件节点的共享机制可以减小规则树尺寸,从而减少内存占用。
树的最底端,就是具体的规则名。在规则树运行时,所有的业务对象通过顶端名为Enter的入口节点插入到规则树,根据连线评估条件节点是否满足,如果能到达最底端规则节点,则说明与当前规则相关的所有条件满足,对应规则里定义的动作就进入待执行状态。
利用树的特点,再配合相关的短路计算方式,可以最大程度上保证规则条件计算性能,减少不必要的条件匹配尝试,从而使得规则的计算性能可以与普通硬编码相媲美,实现业务计算的毫秒级响应。
循环规则
循环规则,它是一种可以对集合对象进行循环执行的规则。它的名称与属性与普通规则一样,接下来是“循环对象”属性,要求我们指定一个集合类型的对象,这个对象可以是个参数或变量 ,规则在运行时将对这里选择的集合进行迭代;下面是“开始前动作”属性,顾名思义,就是在循环规则执行前做的一些动作,通常我们会在这个地方做一些初始化的动作,比如临时参数的初始化赋值等,同样这里的动作可以是0~n个,如果不定义那么就不执行。
接下来是若干个循环规则的循环单元部分,对于一个循环规则来说可以有一个或多个循环单元,每个循环单元都是一个普通规则的规则体,也就是一个由如果、那么、否则三部分构成的普通,定义方式与普通规则完全相同。
在循环规则执行时,每迭代一次“循环对象”,就会将当前迭代的对象插入到工作区,尝试匹配循环单元里的每一个规则体,如果满足条件就执行;最后是“结束后动作”部分,它在循环执行完成后执行,动作可以是0~n个,不定义就不执行。
在决策集设计器中,点击工具栏上的“添加循环规则”按钮就可以添加一个循环规则,如下图所示:
我们来看一个例子,通过这个例子来学习循环规则的使用方法。
示例
我们要实际的业务是统计用户对象里订单金额小于1000的数量以及订单金额大于等于1000的数量。
首先我们需要在Customer实体类里添加一个新的属性orders,如下代码所示:
@Label("订单")
private List<Order> orders;
Order类源码如下:
package com.bstek.entity;
import com.bstek.urule.model.Label;
/**
* @author Jacky.gao
* @since 2016年9月30日
*/
public class Order {
@Label("名称")
private String name;
@Label("价格")
private float price;
@Label("数量")
private int amount;
//省略getter和setter方法
}
打开我们的customer变量库文件,在"会员"分类下添加orders变量,同时将新的Order对象添加到库中,如下图:
这样,“会员”与“订单”之间就形成了一个一对多的关系。接下来我们需要利用循环规则实现一个简单的小需求,那就是统计当前会员的符合条件的订单价格的总额,我们目前定义的两个BOM对象中,没有哪个属性可用于存储订单数量,所以需要添加两个临时的参数值,用来存储小于1000的订单数和大于或等于1000的订单数,如下图所示。
库文件准备好了之后,接下来我们就可以来定义一个循环规则来实现统计总价的需求了,定义好的规则如下:
我们定义的这个循环规则中,对会员对象的订单属性进行循环,在循环开始前我们添加了两个开始前动作,分别对两个用于统计数量的参数作了为零的初始化操作;接下来是循环的循环体部分,循环体中有两个规则体,分别用于统计金额小于1000的订单数量和金额大于或等于1000的订单数量;最后在循环结束后的动作里添加了将两个统计数量的参数输出到控制台的动作。
点击“知识包”节点,添加一个用于测试当前这个包含循环规则的决策集文件,然后将这个规则集文件添加到这个知识包中。
点击工具栏“快速测试”按钮,对当前循环规则包进行测试,在仿真测试窗口中,我们双击会员的订单属性输入框,在弹出的窗口中选择类型为“订单”,如下图所示:
确认后,会弹出“订单”对象的列表,我们可以添加几条,这样就实现了为“会员”这个对象添加若干个订单子对象的功能,如下图所示:
确认后,可以看到会员的订单属性值是一个JSON字符串,这个JSON串表示的就是具体的订单列表值。
点击“测试决策包”,就完成了订单总计统计功能,切换到“参数”,可以看到两个用于统计订单数量的参数值已有了结果,如下图所示:
再看我们的控制台,也已经输出了两条订单数量信息,如下图所示:
在循环规则当中,有些时候我们可能需要从子对象中找出一个符合条件的就可以了,这样后面的就不需要循环了,针对这种情况,URule里还提供了一个名为“跳出循环”的方法,我们只需要在循环规则体的那么中添加这个方法即可,如下图所示:
在我们这里的循环规则中,针对用于统计数量的两个参数,在循环规则开始前添加两个动作对它们进行了初始化,将它们的值设置为0。实际上在URule Pro当中,对于参数的值引擎默认会进行初始化,比如Integer类型的参数它的默认值就是0,所以上面的两个初始化的动作实际上是可以省略的。下表中罗列了参数中会被引擎初始化的数据类型的默认值。
数量类型 | 初始化后的默认值 |
---|---|
Integer | 0 |
Long | 0 |
Double | 0 |
Float | 0 |
Boolean | false |
List | 一个不含任何元素的ArrayList对象 |
Set | 一个不含任何元素的HashSet对象 |
Map | 一个不含任何元素的HashMap对象 |
规则集中提供了规则模版导入功能。在业务规则的编写过程中,可能存在一些功能类似的普通规则或循环规则,比如某些规则只是条件部分有一点变化,或者动作部分不太一样,其它都是相同的,对于这种类型的我们没有必要再重新定义一次,只需要把功能类似的普通规则或循环规则保存为模版,然后在需要时将其导入即可。利用这种特性可以大大提高业务规则的定义效率,节省开发时间。
在规则集中,每个普通规则和循环规则名前都有一个复选框,我们可以根据需要勾选后点击工具栏上的“保存为模版按钮”,如下图所示:
在弹出的窗口中输入模版名称及相关备注信息后即可,如果当前没有模版分类可以先创建一个分类(模版分类是为了更好的管理规则模版),然后选中后即可保存。
有了规则模版后,接下来就可以打开需要导入模版的规则集文件,选择工具栏上的“从模版导入”按钮,就可以将选择的规则模版导入到当前规则集文件中。
规则模版的适用范围是当前项目,也就是说当前项目中定义的模版在当前项目中的任何一个规则集文件中都可以导入使用,有了规则模版可大幅减少重复劳动,提高开发效率。
对象名与属性名的分隔
从2.1.1版本开始,在向导式规则中引用变量或常量时,分类名与具体属性名之间的分隔符支持用户自定义,其中变量默认为“的”;常量默认为空(分隔符为空时常量只显示常量值而不显示分类),如下图所示:
在使用过程中,如果觉得默认提供的分隔方式看起来不满足要求,那么可以通过修改下面两个属性来实现对变量以及常量分隔符的自定义功能:
属性名 | 默认值 | 描述 |
---|---|---|
urule.variable.link | 的 | 用于定义向导式规则中变量分类与属性之间的分隔符 |
urule.constant.link | 空 | 用于定义向导式规则中常量分类与属性之间的分隔符,因为该属性默认值为空,所以我们看到默认向导式规则中常量不显示分类,只显示值,如果给该属性定义具体值,那么常量的分类就会显示出来 |
条件模版
从2.1.9版本开始,可以在“规则集”分类中创建“条件模版”文件,条件模版的作用就是把一些通用条件进行归类,并定义好有意义的名称,这样在向导式的规则集文件里就可以引用条件模版文件并在规则条件中使用条件模版,一个创建好的条件模版文件效果如下图所示:
条件模版创建完成后,就可以在向导式规则文件里引用并使用了,引用方式与导入库文件的方法一致,点击工具栏上的“模版”按钮,选择其下的“条件模版”菜单项,然后选择并添加相应的条件模版文件即可,如下图所示:
使用导入的条件模版,只需要在规则条件里选择“添加条件模版”即可,如下图所示:
从2.1.9版本开始,对于向导式规则,在规则名边,还添加了一个可以用于直接查看当前规则在编译后条件树的按钮,这对于采用了条件模版的规则来说非常有意义,通过查看编译后的条件树,就可以明确在添加了条件模版后对当前规则的条件组合产生的影响,
从而可以帮助我们更好的理解规则中配置的条件。
动作模版
从2.2.1版本开始,向导式规则集中开始支持动作模版。可以在“规则集”分类下创建“动作模版”文件,在动作模版文件中,可以把一些通过的动作定义出来,并赋予有意义的名称,这样在向导式规则集文件中就可以引用动作模版文件并在规则的动作部分使用它, 动作模版文件如下图所示:
定义好动作模版文件后,就可以在向导式规则文件里引用并使用了,引用方式与导入库文件的方法一致,点击工具栏上的“模版”按钮,选择其下的“动作模版”菜单项,然后选择并添加相应的动作模版文件即可;具体使用方法与条件模版相同,这里就不再赘述。
规则集的运行模式
在2.1.7版本以前,向导式规则集在默认模式下会把当前文件中所有所有规则编译成一棵规则树,根据输入对象先进行条件匹配计算,计算好后再根据规则设定的优先级来执行所有匹配规则的动作部分。
如果存在两个规则A、B,A规则的优先级高于B,A规则根据输入默认对象属性值条件可以满足,而B规则根据输入对象条件不满足,这时执行A规则动作部分,在动作中将输入对象某属性改为另外一个值,经过这个修改, B规则此时条件满足了,但在默认模式下,所有规则的条件匹配动作已经完成了,所以在A规则的动作部分将输入对象某属性改为B规则满足的值后,B规则也不会尝试重新匹配条件以执行B规则条件满足后的动作部分。
要实现这一功能,在2.1.7之前的版本当中,我们只能在A规则条件满足后的修改输入对象某属性值的动作后添加一个“更新工作区”的函数,更新这个修改后的输入对象,以使这个对象能重新匹配所有的规则, 看看有没有满足条件的,这样B规则才会被重新激活;或者使用“执行组”属性也能达到同样的目的,也就是给B规则添加一个“执行组”属性,在A规则条件满足后的修改输入对象某属性值的动作后加上激活B规则对应的执行组。
可以看到,在默认模式下,对于存在大量相互依赖的向导式规则来说,上述两种方式用起来还是很麻烦的,为此从2.1.7版本开始,对于向导式规则集文件添加了一种新的“顺序”运行模式, 通过向导式规则集的工具栏上方运行模式按钮,即可实现将默认模式修改为“顺序模式”,如下图所示:
在这种模式下,当前规则集里的所有规则将不会被编译成一整棵规则树,而是每个规则都会独立的编译成一个规则树,运行的时候,会根据规则定义的优先级属性依次运行这些规则树, 这样,对于存在上述逻辑情况的业务来说,我们只需要修改运行模式为“顺序模式”,同时为各个规则定义好优先级,那么它们就会逐个执行,优先级较高的规则先执行, 执行后的动作部分如果存在对业务数据的修改,就会直接影响到后面规则条件的匹配,不再需要使用“更新工作区”以使规则重新尝试匹配或者使用“执行组”属性对规则执行顺序进行编排,这样可以大大简化业务规则定义的复杂度。
需要指出的是“顺序模式”下,因为规则是一条条匹配,所以性能上相比“默认模式”要差一些,如没有特殊需要不建议采用“顺序模式”。
更多建议: