Drool学习记录(二) Kie Session、Truth maintenance

参考Drools官方文档(3.1 KIE Session和3.2 Inference and truth maintenance in the Drools engine),学习关于Kie Session和Truth maintenace的内容。这两节内容虽然很基础,但是感觉官方文档说的还是不够明了,尤其是Stateless Session和Stateful Session的区别,和insert()和insertLogical()的区别,官方文档给出的样例没有很好的体现出来,下面我尝试用我自己的例子来理解一下

1 Stateless Session和Stateful Session的区别

按照官方解释,Stateless Session每次调用会话是独立的,不保存上次调用的数据状态,而Stateful Session正相反。相对来说,Stateful Session更加容易理解,也更为常用,我们先写一个Stateful Session的例子。
先定义一个Person类

package drools.samples.domain;

public class Person {
    private String name;
    private int age;

    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

再定义一个规则文件statefulSampleRule.drl,逻辑很简单,如果一个person小于18岁,打印"xxx is a child",否则打印"xxx is a adult"。

package drools.samples.rules.statefulSampleRule

import drools.samples.domain.Person

rule "Age < 18"
when
  $a : Person(age < 18)
then
  System.out.println($a.getName()+" is a child");
end

rule "Age > 18"
when
  $a : Person(age >= 18)
then
  System.out.println($a.getName()+" is a adult");
end

在kmodule.xml里定义一个名为"statefulTest"的ksession

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="stateful" packages="drools.samples.rules.statefulSampleRule">
        <ksession name="statefulTest"/>
    </kbase>
</kmodule>

写个执行程序,我们先把这个person对象的年龄设成16,执行下rule,然后我们再动态地更新年龄为25,重新执行下rule

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("statefulTest");

Person p1 = new Person("Tom", 16);
FactHandle factHandle=kSession.insert(p1);
kSession.fireAllRules();
p1.setAge(25);
kSession.update(factHandle, p1,"age");
kSession.fireAllRules();

运行结果


6.PNG

所以在一个stateful session里,允许我们更新fact的状态并重新触发规则evaluate。
我们继续写个stateless session的例子。定义一个规则文件statelessSampleRule.drl,其实和statefulSampleRule.drl一样

package drools.samples.rules.statelessSampleRule
import drools.samples.domain.Person
rule "Age < 18"
when
  $a : Person(age < 18)
then
  System.out.println($a.getName()+" is a child");
end

rule "Age > 18"
when
  $a : Person(age >= 18)
then
  System.out.println($a.getName()+" is a adult");
end

在kmodule.xml里定义一个名为"statelessTest"的ksession,主要加上"stateless"的属性

<kbase name="stateless" packages="drools.samples.rules.statelessSampleRule">
        <ksession name="statelessTest" type="stateless"/>
 </kbase>

执行程序和结果

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
StatelessKieSession kSession = kContainer.newStatelessKieSession("statelessTest");

Person p1 = new Person("Tom", 16);
kSession.execute(p1);
7.PNG

你可能会问为什么我没有在stateless的例子里去更新age,这是因为查看StatelessKieSession的接口,你会发现根本就不提供更新fact的操作,并且stateless session的接口只提供了execute接口,因此stateless session的不同点就表现在这里,你无法在一个stateless session里去更新fact,一个fact只会有一次存入Working memory的操作。
我看网上有人认为stateless session在执行规则时会忽略fact的更新从而不重新触发rule,在我看来不是这样的。我们修改下statelessSampleRule.drl

package drools.samples.rules.statelessSampleRule
import drools.samples.domain.Person

rule "Age < 18"
when
  $a : Person(age < 18)
then
  System.out.println($a.getName()+"'age is less 18");
end

rule "Age 16"
when
  $a : Person(age == 16)
then
  System.out.println($a.getName()+"'age is 16");
  modify($a){setAge(25)}
end

rule "Age 25"
when
  $a : Person(age == 25)
then
  System.out.println($a.getName()+"'age is 25");
  modify($a){setAge(17)}
end

在这个规则文件里,我们特意指定fact进行更新,看看stateless session会不会处理这种状况。执行结果:


8.PNG

可以看到stateless session仍然正确处理了中间的fact变化,结果其实和stateful session是一样的,stateless session只是在你第二次调execute时不会和第一次调execute有关联。

2 insert()和insertLogical()的区别

2.1 Inference

在Drools规则语法中,insert操作的目的是为了进行推断(inference),即根据已有fact推断一些中间fact,由此进一步的决定action。站在程序员的角度,其实就相当于生成中间变量。例如我们现在要删选出名为Tom的child,我们可以这样写规则:

rule "A child named Tom"
when:
  $p : Person(name=="Tom",age<18)
then
  System.out.println("The child Tom is here");
end

这个规则的condition里有age和name两个判断条件,这样写在这个例子没问题,但是如果考虑规模的提升和后期的维护,我们应该拆成两个condition,那么我们就可以用insert在执行过程中生成中间fact。我们像下面这样写规则,注意IsChild我们同样需要在引擎外定义好。

rule "Age < 18"
when
  $p : Person(age < 18)
then
  insert(new IsChild($p));
  System.out.println($p.getName()+" is a child");
end

rule "Name is Tom"
when:
  $p : Person(name=="Tom")
  IsChild(person==$p)
then
  System.out.println("The child Tom is here");
end

2.2 insertLogical

简单情况下,用insert就足够了,但更多的情况下应该用insertLogical方法。我们看看官网的说明:insertLogical执行逻辑插入操作,通过Drools内置的truth maintenance手段,当逻辑插入的fact不再满足规则的条件时将自动撤销(retracted)。依旧有点难以理解,而且如果你自己写demo测试的话很多时候用insertLogical和insert的结果是一样的。按照我的理解,这两者的关键区别是在是否自动撤销,也就是说用insert插入到Working memory的fact,除非你手动撤销,不然的话是一直存在的,你在更新一个fact后,更新前的状态仍然存在;而insertLogical则会自动撤销掉所有不满足规则条件的fact。
我们先用insert写个这样的规则,在判断person是child还是adult后插入一个IsChild或者IsAdult的fact,接着判断当前空间是否存在IsChild。先定义好IsChild和IsAdult类:

public class IsChild {
    private Person person;

    public IsChild(Person person){
        this.person=person;
    }
    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
}
public class IsAdult {
    private Person person;

    public IsAdult(Person person){
        this.person=person;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
}

规则文件:

rule "Age < 18"
when
  $p : Person(age < 18)
then
  insert(new IsChild($p));
  System.out.println($p.getName()+" is a child");
end

rule "Age >= 18"
when
  $p : Person(age >= 18)
then
  insert(new IsAdult($p));
  System.out.println($p.getName()+" is a adult");
end


rule "no child fact now"
when
  not(IsChild())
then
  System.out.println("There is no child fact now");
end

然后我们输入一个16岁的person,执行后再更新成25岁再次执行:

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("insertTest");

Person p1 = new Person("Tom", 16);

FactHandle factHandle=kSession.insert(p1);
kSession.fireAllRules();

p1.setAge(25);
kSession.update(factHandle, p1,"age");
kSession.fireAllRules();
9.PNG

可以看到运行的结果没有符合我们的预期,明明Tom的年龄更新成了25岁,引擎却没有输出"There is no child fact now",也就是说仍然有IsChild的fact存在
我们再用insertLogicl重写下规则:

rule "Age < 18"
when
  $p : Person(age < 18)
then
  insertLogical(new IsChild($p));
  System.out.println($p.getName()+" is a child");
end

rule "Age >= 18"
when
  $p : Person(age >= 18)
then
  insertLogical(new IsAdult($p));
  System.out.println($p.getName()+" is a adult");
end

rule "no child fact now"
when
  not(IsChild())
then
  System.out.println("There is no child fact now");
end

同样的主程序运行一遍,其效果如下:


10.PNG

可见逻辑插入会自动撤销掉IsChild的fact,其结果符合我们的预期。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • Drools是一款基于Java的开源规则引擎 实现了将业务决策从应用程序中分离出来。 优点: 1、简化系统架构,优...
    生活的探路者阅读 11,043评论 0 5
  • 深入了解Drools 简单介绍 笔者正在做风控系统,风控系统里边存在非常多的规则(比如:age < 16 || a...
    duval阅读 22,377评论 2 19
  • 概述(Overview) 以.drl为扩展名的文件,是Drools中的规则文件,规则文件的编写,遵循Drools规...
    老羊_肖恩阅读 41,741评论 4 31
  • 1 关于规则引擎 基于知识库和规则的专家系统是早期最主流的人工智能,不同于现在流行的基于统计、机器学习的智能算法,...
    11c170319da1阅读 716评论 0 1
  • 时间过得飞快,这一百多天没看书复习,其实也没有做成其他的什么事情,时间就是这样,不知不觉就消失了。 珍惜,充分利用...
    十一珍珠阅读 156评论 0 0