规则引擎又称为决策引擎,具体实现有很多种形式,比如由单个规则构成的规则集、用于评分的评分卡、多维度条件计算的决策表等;无论形式上怎么变化,归纳下来无非就是“如果…那么…否则”三大部分。
在“如果”和“那么”之间,用于存放规则的条件;“那么”之后定义若干个动作,这些动作在条件满足后执行;“否则”的后面也是若干个动作,这些动作在“如果”和“那么”之间条件不满足的时候执行。
当然也不是所有规则引擎都有“如果…那么…否则”三大部分,比如Drools里只有“如果…那么”没有“否则”,这在使用的时候多少会带来一些麻烦,这点在使用的时候就会发现。
前面提到,无论规则引擎产品提供了多少种规则工具,总结下来每种规则工具实现的逻辑都是“如果…那么…否则”三大部分,下图当中展示了URule这款规则引擎产品向导式规则集的结构,可以非常明显看到规则结构的三大部分。
在上图中的两个规则当中,因URule提供的规则条件部分是图形化方式展现,所以条件阅读起来就容易的多,“那么”和“否则”部分定义了一条条的动作,分别在条件满足和不满足的时候执行。
在Drools中,规则体部分就没有“否则”,如下所示:
rule "rule1"
when
$order:Order(items not contains "手机");
then
System.out.println($order.getName());
end
因为Drools没有提供类似URule中的可视化向导方式构建规则,只有上面这种脚本方式定义规则,所以它的规则看起来和写代码区别不大,但这并不影响对其规则结构体的观察。
可以看到,Drools的规则体结构中没有“否则”部分,也就是说规则在条件不满足的时候什么也不能做,有人说这是老外对规则的理解就是这样,我个人觉得Drools之所以设计成这样,应该是和其实现的Rete算法有关,在Rete算法当中只有根据条件得出结果,没有关于条件不满足的介绍,所以Drools中没有“否则”。
Drools中的规则以脚本方式呈现,相对于使用者来说增加了学习成本,需要像学习一门编程语言一样去学习Drools语法,所以它的使用对象多数都是程序员,普通的业务人员很难上手,在这一点上,URule提供的图形化编辑方式有较大的优势。但因为Drools采用的是脚本方式定义规则,所以Drools的灵活性也较URule的向导方式定义规则好很多,比如下面这段:
rule "test rule"
when
$girls: List() from collect(Person(sex == "female"))
then
System.out.println($girls.size() + " grils.");
end
上面的这个规则当中表示要从一个集合中取出所有性别为女性的人,并打印出这些人的数量。对于这个规则如果要在URule里实现,那么就需要用到循环规则,相对来说工作量要多一些,但在Drools中,因为其用脚本方式表现,所以写起来容易的多。
评分卡是一种类型的业务规则表现形式,需要规则引擎或决策引擎提供评分卡工具才行,比如在Drools中就没提供专门用于评分的评分卡,所以如果使用Drools,同时要用其进行评分,那么只能写一条条的规则来代替,工作量可想而知。
URule中提供了两种类型的评分卡工具,下面是URule中一张普通评分卡:
URule的评分卡就是一个表格,由属性列、条件列和动作列三部分构成,我们再来看看IBM ODM的评分卡,如下图所示:
无论是URule提供的评分卡,还是ODM中的评分卡,仔细看,其实这个表格的每一行都是一个标准的规则,比如上图中第一行就可以转换成下面这样的规则:
rule "name"
when
age>=18 && age<=25
then
score=75
end
每一行对应一条规则,一张评分卡里有若干行,实际就对应若干条规则,规则引擎在运行的时候就会把表格中每一行拿出来构建成一个标准的规则,实际执行的时候就运行这些规则就好。
评分卡里每一行对应的规则,和标准规则相比,没有“否则”部分,从这里也就更加能理解Drools里的标准规则没有“否则”部分定义了。
和评分卡一样,决策表也是一种类型的规则工具,我们首先来看看Drools中提供的决策表的样子,如下图所示:
这里需要说明的是,Drools本身没有提供决策表的编辑器,转而借助的是Excel里实现决策表功能,所以上图当中就是一个定义在Excel中符合Drools规范的决策表示例。
可以看到,在这个借助Excel定义的决策表中,它实际上就是把Drools里标准规则脚本移到了Excel的单元格中,只要了解它的脚本语法,那么这个表格规范也就很容易理解。
由于ODM中有专门的决策表设计工具,所以相比在Excel中定义看起来就清晰很多。决策表除最后一列是动作部分,前面两列是条件部分,最后一列在前面条件列满足的情况下执行。
和评分卡类似,它的每一行都可以构成一个标准的规则,以上图第一行为例,运行时可生成的标准规则示意结构如下:
rule "name"
when
年龄 >=0 && 年龄<18
then
审批结果="APPROVE"
end
最后再来看看urule中的决策表,如下图所示:
这种决策表结构更为直观,它同样也是一行构成一条件标准的规则。
从上面我们举的关于评分卡和决策表两个例子来看,无论规则引擎提供什么样的设计工具,其在运行时都是要将这些表现形式编译成标准的由“如果…那么…否则”构成的标准规则体结构。
所以我们要设计一款规则引擎,从核心的角度来看,只需要设计好由“如果…那么…否则”构成的标准规则体结构,同时处理好这种标准的结构在运行时采用什么样的算法去处理才能最为高效即可。
看看下面这个标准的规则体:
rule "name"
when
年龄 >=0 && 年龄<18
then
审批结果="APPROVE"
end
这其中无论是“那么”中的动作,还是“否则”中的动作(上面的规则体中没有否则),其实都是一条条需要引擎是实际执行的,这个在编写算法的时候比较简单,没有什么花样;唯一需要我们注意的就是“如果”部分,也就是规则的条件部分。
条件算法,也叫模式匹配算法,是规则计算的核心,算法的好坏直接影响规则计算的性能,所以我们会看到各种各样的模式匹配算法,比如最有名的RETE,也就是Drools中实现的那套算法,当然在其它的诸如IBM的ODM及URule这些商用决策引擎产品里也都实现了自己各自的RETE算法,关于RETE算法的一些基本原理,网上有很多介绍,这里就不再赘述了。
RETE算法的基本原理,在实际实现过程中会发现很多问题,还需要灵活应对,所以各家在实现RETE算法时都为自己的RETE算法取有特有的名字,比如Drools里叫RETEOO、ODM里叫RETEPlus等。