Drool學習記錄(二) Kie Session、Truth maintenance

參考Drools官方文檔(3.1 KIE Session和3.2 Inference and truth maintenance in the Drools engine),學習關于Kie Session和Truth maintenace的內容。這兩節(jié)內容雖然很基礎,但是感覺官方文檔說的還是不夠明了,尤其是Stateless Session和Stateful Session的區(qū)別,和insert()和insertLogical()的區(qū)別,官方文檔給出的樣例沒有很好的體現(xiàn)出來,下面我嘗試用我自己的例子來理解一下

1 Stateless Session和Stateful Session的區(qū)別

按照官方解釋,Stateless Session每次調用會話是獨立的,不保存上次調用的數(shù)據狀態(tài),而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;
    }
}

再定義一個規(guī)則文件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>

寫個執(zhí)行程序,我們先把這個person對象的年齡設成16,執(zhí)行下rule,然后我們再動態(tài)地更新年齡為25,重新執(zhí)行下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的狀態(tài)并重新觸發(fā)規(guī)則evaluate。
我們繼續(xù)寫個stateless session的例子。定義一個規(guī)則文件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>

執(zhí)行程序和結果

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的接口,你會發(fā)現(xiàn)根本就不提供更新fact的操作,并且stateless session的接口只提供了execute接口,因此stateless session的不同點就表現(xiàn)在這里,你無法在一個stateless session里去更新fact,一個fact只會有一次存入Working memory的操作。
我看網上有人認為stateless session在執(zhí)行規(guī)則時會忽略fact的更新從而不重新觸發(fā)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

在這個規(guī)則文件里,我們特意指定fact進行更新,看看stateless session會不會處理這種狀況。執(zhí)行結果:


8.PNG

可以看到stateless session仍然正確處理了中間的fact變化,結果其實和stateful session是一樣的,stateless session只是在你第二次調execute時不會和第一次調execute有關聯(lián)。

2 insert()和insertLogical()的區(qū)別

2.1 Inference

在Drools規(guī)則語法中,insert操作的目的是為了進行推斷(inference),即根據已有fact推斷一些中間fact,由此進一步的決定action。站在程序員的角度,其實就相當于生成中間變量。例如我們現(xiàn)在要刪選出名為Tom的child,我們可以這樣寫規(guī)則:

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

這個規(guī)則的condition里有age和name兩個判斷條件,這樣寫在這個例子沒問題,但是如果考慮規(guī)模的提升和后期的維護,我們應該拆成兩個condition,那么我們就可以用insert在執(zhí)行過程中生成中間fact。我們像下面這樣寫規(guī)則,注意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執(zhí)行邏輯插入操作,通過Drools內置的truth maintenance手段,當邏輯插入的fact不再滿足規(guī)則的條件時將自動撤銷(retracted)。依舊有點難以理解,而且如果你自己寫demo測試的話很多時候用insertLogical和insert的結果是一樣的。按照我的理解,這兩者的關鍵區(qū)別是在是否自動撤銷,也就是說用insert插入到Working memory的fact,除非你手動撤銷,不然的話是一直存在的,你在更新一個fact后,更新前的狀態(tài)仍然存在;而insertLogical則會自動撤銷掉所有不滿足規(guī)則條件的fact。
我們先用insert寫個這樣的規(guī)則,在判斷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;
    }
}

規(guī)則文件:

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,執(zhí)行后再更新成25歲再次執(zhí)行:

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重寫下規(guī)則:

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,其結果符合我們的預期。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Drools是一款基于Java的開源規(guī)則引擎 實現(xiàn)了將業(yè)務決策從應用程序中分離出來。 優(yōu)點: 1、簡化系統(tǒng)架構,優(yōu)...
    生活的探路者閱讀 11,226評論 0 5
  • 深入了解Drools 簡單介紹 筆者正在做風控系統(tǒng),風控系統(tǒng)里邊存在非常多的規(guī)則(比如:age < 16 || a...
    duval閱讀 22,609評論 2 19
  • 概述(Overview) 以.drl為擴展名的文件,是Drools中的規(guī)則文件,規(guī)則文件的編寫,遵循Drools規(guī)...
    老羊_肖恩閱讀 42,373評論 4 31
  • 1 關于規(guī)則引擎 基于知識庫和規(guī)則的專家系統(tǒng)是早期最主流的人工智能,不同于現(xiàn)在流行的基于統(tǒng)計、機器學習的智能算法,...
    11c170319da1閱讀 868評論 0 1
  • 時間過得飛快,這一百多天沒看書復習,其實也沒有做成其他的什么事情,時間就是這樣,不知不覺就消失了。 珍惜,充分利用...
    十一珍珠閱讀 187評論 0 0

友情鏈接更多精彩內容