Drools入门之drl规则结构讲解

上一节我们写了一个Drools的hello world小例子,里面涉及了.drl文件。这一节我们来讲讲它。

drl规则文件

.drl文件,这是其中一种规则文件,也可以是.xml.drls文件,甚至还可以是.xls.xlsx文件。其中最基本的就是.drl文件,下面就对该文件的基本结构作讲解。

规则文件名

规则文件名即hello.drl,其命名规则不像Java那样要求首字母大写。虽然要求并不严格,但对规则文件命名时也最好要规范,见名知意,既方便自己,又方便他人。

规则文件的内容
  1. package:关键字package为三大块中的包路径,这里的包路径是逻辑路径,理论上是可以随便定义的,并且是必填内容。为了更方便地开发,建议在编辑包路径时最好与文件目录同名,类似Java一样以小数点(.)的方式隔开,且必须用小数点隔开。这里要提醒一下读者,在规则文件中关键字package永远在代码的第一行(规则模板除外)。
  2. rule:关键字rule为三大块中的规则体,是核心内容之一,以关键字rule开头,以end结尾,每个规则文件中可以包含多个rule规则体,但rule规则体之间不能交叉使用,即一个rule只能对应一个end。rule的参数是可以随意定义的,rule的参数指规则名,建议读者在编写规则名时以驼峰式命名,虽然有时可以不添加双引号(“”),但还是建议每一个规则名都加上引号,以避免出现编译报错的问题。同一个规则库[4]中相同规则逻辑路径下的规则名不可以相同,可以理解为规则名即是一个ID,不可重复
  3. import:关键字import为三大块中的引用,它与Java引用其他类是一样的,其目的是为了对象类引用。与Java不同的是,在引用静态方法时,需要添加function关键字
规则体说明

规则体是规则文件内容中的核心,分为LHS、RHS两大功能模块。

  1. LHS:条件部分又被称为Left Hand Side(LHS),即规则体when与then中间的部分。在LHS中,可以包含0~n个条件(非常类似Java语法中的if判断语句),如果LHS部分为空,那么引擎会自动添加一个eval(true)条件,由于该条件总是返回true,因此LHS为空的规则体也总是返回true。其内容为:

    rule "test001"
            when
                // 这里为空表示 eval(true);
            then
             System.out.println("hello world");
         end
    
    JAVA
  2. RHS:结果部分又被称为Right Hand Side,即一个规则体中then与end之前的部分,只有LHS部分的条件都满足时RHS部分才会被执行。这里要注意的是,在Rete的算法中,规则在匹配时只会执行LHS为true的规则;加载规则时,会将所有规则体中的LHS部分先执行,即当前规则库中的LHS部分会被先一步加载。例如,当前规则体中的LHS条件为false时也是会被加载的,这可以理解为规则执行前的预加载功能,区别在于规则体的RHS部分不进行运算。只有Fact对象发生了改变,规则体才有可能重新被激活,之前为false的LHS就有可能变成true。

    RHS才是规则体真正做事情的部分,即要处理和返回业务结果的部分。可以将条件满足而触发的动作写在该部分,在RHS中可以使用LHS定义绑定变量名、设置全局变量,或者直接编写Java代码(对于要用到的Java类或静态方法需要在规则文件中用import将该类引入后才能使用,这与Java文件的编写原则相同)。规则体中的LHS部分是用来放置条件的,RHS部分是编写满足条件后处理结果的,虽然RHS部分可以直接编写Java脚本,但不建议在RHS中有条件判断。如果需要条件判断,那么要重新考虑将其放在LHS中,否则就违背使用规则的初衷了。

  3. Fact。上述说明中提到了一个关键字Fact,它在规则引擎中是非常重要的,在介绍对象引用章节之前,必须先将其概念性的知识点进行一个说明。它也是一个必须要理解的概念。

    Drools规则引擎中传递的数据,术语称Fact对象。Fact对象是一个普通的JavaBean(不只是JavaBean对象,也可以是任何Object对象),规则体中可以对当前对象进行任何的读/写操作,调用该对象提供的方法。当一个Fact(JavaBean)插入Working Memory(内存储存)中,规则体使用的是原有对象的引用(并不是克隆,与Java变量性质相似),规则体通过操作Fact对象来实现对应用数据的管理,对于其中的属性,需要提供getter setter或“Object”的可操作方法。执行规则时,可以动态地向当前Working Memory插入、删除或更新Fact对象。

    规则进行计算时需要用到应用系统中的数据,先将这些数据设置到Fact对象中,然后将其插入规则的Working Memory中,一个Fact对象通常是一个具有getter方法和setter方法的POJO对象,由Java代码进行insert操作。通过getter方法和setter方法可以方便地对Fact对象进行操作,所以可以更通俗地把Fact对象理解为规则与应用系统数据交互的桥梁或通道。

    当Fact对象插入Working Memory后,会与当前Working Memory中所有的规则进行匹配,同时返回一个FactHandler对象。FactHandler对象是插入WorkingMemory中Fact对象的引用句柄,通过FactHandler对象可以实现对相应的Fact对象通过API进行删除及修改等操作。

    在RHS中提供了一些对当前Working Memory实现快速操作的宏函数或对象,如insert/insertLogical、update/modify与retract/delete。通过这些函数可以实现对当前Working Memory的Fact对象进行新增、修改或删除的操作。如果读者还要使用Drools中提供的其他方法,那么也可以使用其他的宏函数进行更多的操作。

对象引用

关键字import是用来导入规则文件需要使用的外部对象,这里的使用方法与Java相似,与Java不同的是,import不仅可以引入类,也可以导入类中的某一个可访问的静态方法。

创建一个Person类

package com.domain;

public class Person {
    private Integer age;

    private String name;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
JAVA

然后我们新增一条规则test002

package rules
import com.domain.Person

rule "test001"
        when
            eval(true);
        then
         System.out.println("hello world");
end
rule "test002"
        when
            $p:Person()
        then
         System.out.println("输出引用对象"+$p);
end
JAVA

在代码中注入fact对象

public class RulesHello {
    public static void main(String[] args) {
        KieServices kieService = KieServices.Factory.get();
        KieContainer kieContainer = kieService.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("testhelloworld");
        Person person = new Person();
        person.setAge(20);
        person.setName("张三");
        kieSession.insert(person);
        int count = kieSession.fireAllRules();
        System.out.println("总共执行了"+count+"条规则");
        kieSession.dispose();
    }
}
JAVA

其中kieSession.insert(person);就是注入fact对象。

运行代码,观察输出日志,可以看到test002已被执行

hello world
输出引用对象Person{age=20, name='张三'}
总共执行了2条规则
SHELL

条件

判断张三的年龄是否是20岁,新增一条规则

rule "test003"
        when
            $p:Person(age == "20",name == "张三")
        then
            System.out.println("张三的年龄为20");
        end
JAVA

Drools入门之drl规则结构讲解
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/drools-drl
作者
卑微幻想家
发布于
2022-06-16
许可协议