1. 程式人生 > >[Drools]JAVA規則引擎 -- Drools

[Drools]JAVA規則引擎 -- Drools

Rete演算法是Charles Forgy在1979年的論文中首次提出的,針對基於規則知識表現的模式匹配演算法。目前來說,大部分規則引擎還是基於rete演算法作為核心,但都有所改進,比如drool,jess等等,下面介紹rete演算法的概念,一些術語,以及使用規則引擎需要注意的問題。

先來看看如下的表示式:

     (name-of-this-production
        LHS /* one or more conditions */
       -->
        RHS /* one or more actions */
       )

    name-of-this-production就是規則,LHS(left hand side)一系列條件,RHS(right hand side)這個是我們滿足條件後應該執行的動作。

   結合該圖介紹幾個概念:

    production memory(PM)是由所有的production形成。

    working memory(WM)由外界輸入根據匹配演算法形成的,反映了執行中的規則引擎的狀態,記錄各種資料, WM中每一個item稱為working memory element(WME) ,由外界的輸入產生。

     agenda負責匹配,衝突解決,執行動作。

    rete是網路的意思(拉丁語),它將所有的規則最終解釋(或編譯)生成一個識別網路,其中包括了alpha網路,beta網路。alpha網路是根據LHS生成的網路,它根據外界的輸入快速識別是否存在condition成立,並且跟其beta網路互動更新整個網路的狀態,如下圖:

    最基本的alpha網路就如上圖所示,類似於這樣,所有的condition被parse到這樣的網路,當外界輸入wme時,該wme會進入這樣一個網路進行辨識,如果到達最底端,證明一個condition成立了,當然,如圖這個網路算是最簡單的實現了,實際規則引擎需要提供更快速的演算法去辨識輸入的wme,比如將圖中color的各種值存入hashtable,或者是jumptable,又或者是trie tree。整個alpha network是一個巨大的字串匹配和過濾網路,需要將各種資料結構組合在一起去實現海量condition情況下的快速匹配。各種規則引擎的實現又是不一致的,比如jess,如下圖:

    (defrule done
       (TESTING)
       (number ?number)
       (TEST IS DONE)
      (INIT CREDIT 5)
      (CUSTOMER AGE ?age)
       (has ?type "PP"))
=>
      (assert (TEST COMPLETED)))

     


這個production的解釋後生成的網路,這裡我們先注意紅色的節點,這些節點就是alpha網路的節點,這個圖只是描述了大致的過程,以第一列為例,第一個紅色node表示輸入是否匹配TESTING這個字串,第二個node匹配在TESTING後面的引數數量(slot)是否匹配0,如果我們assert TESTING進入WM,那麼這個fact是可以匹配到done這個rule的第一個condition的,其他可以依次類推,值得注意的是最後一個condition,has是我們自定義的function,類似這樣的function,jess沒有單獨生成一列,只是將它作為CUSTOMER AGE ?age這一列的最後一個node,這樣的condition有個特點就是需要執行一段程式碼去判斷某個事實是否成立(不僅僅只是做字串的操作),這段程式碼不僅僅是字串的匹配,同時還具有實時性,類似這樣的condition開發中需要注意,因為alpha network在執行期會不止一次去執行這個condition是否成立,這個是匹配演算法的特性決定,所以,我們需要用cache或者規則語言的特性去避免不必要的執行code以提高效能。

下面貼個比較複雜的例子:

  


   圖太大,一個截不下來。。。。。。

   下面我們結合兩個例子說說beta網路,當alpha網路過濾後condition成立,WME被傳遞到beta網路時,綠色的node就要發揮作用了,這個node就是join node,它有兩個input,一個join node ,一個alpha node(紅色),join node是由多個WME組成的,對於初始的join node 我們稱為left input adapter 如圖中黃色的node,該node是空的,那麼第一個把這個node作為left input的join node就只包含了一個WME,下一個join node則包含了兩個WME,以此類推。圖中天藍色的node上方的join node 完全匹配了production執行需要的condition,所以這個rule就被啟用等待執行了。

    假設我們需要編輯業務邏輯,那麼最好的描述載體就是流程圖,簡單的流程圖包含以下一些基本單元:起始節點,邏輯判斷,執行動作,結束節點。這些節點可以完成最簡單的業務邏輯描述,那麼我們把這些流程parse到規則的時候,我們會怎麼去做,第一個邏輯判斷單元返回true,於是我們執行某個動作,第二,三個邏輯判斷單元返回true我們執行某個動作,相當於會parse到兩個規則,符合condition1,production1觸發,符合condition2,3,production2觸發,有了beta網路,我們在觸發production2時只需要判斷condition2,3是否成立,對於更復雜的情況,beta網路提高了速度,避免了重複匹配。

    開發中使用規則引擎也遇到些問題,總結如下:

    1)規則引擎中對於特殊condition的處理,由於condition會在部分production中重複出現,所以會造成condition的重複匹配問題,影響了程式的效能,這個要結合專案去優化rule指令碼的parse或者使用cache去提升效能。

    2)記憶體消耗問題,rete演算法是空間換時間,所以對於記憶體的消耗是比較大的,特別是載入rule的時候(生成網路),在執行期記憶體會緩慢增長,所以gc效率需要注意,同時單個伺服器所能承受的壓力(多個WM)也跟規則引擎息息相關。

    3)測試,對於使用規則去表達業務的系統,如何測試是必須解決的問題,對於這個問題,也只能保證基本的流程分支覆蓋測試,對於複雜情況下的defect很難發現,不過有些原則需要注意,如果要使用規則引擎,我們必須完全以規則引擎為核心,對於業務邏輯必須儘可能的抽取到規則引擎去實現,對於擴充套件實現的function粒度必須小且簡單,不要再程式碼中去實現業務邏輯。

    4)大部分的condition需要是不變的,也就是說基本資訊需要保持穩定不變。比如某客戶公司上屬集團信用額度大於100w這樣的condition,這個額度變化的頻度不會很高,不需要去實時匹配。

    5),remove WME production是較複雜的操作,規則較複雜時,應該儘量少去做這樣的操作。

====================================================關於rete演算法轉載的資料================================================

最近面試的時候,經常被問及自己參加的專案中rete演算法的原理,但是RETE演算法是一個比較複雜的演算法,在短時間內不能闡述的足夠清晰,在這裡做個簡單的介紹。轉載請註明來自 http://chillwarmoon.iteye.com
RETE
演算法是一個用來實現產生式規則系統的高效模式匹配演算法。該演算法是由卡內基美隆大學的Charles L. Forgy1974年發表的論文中所闡述的演算法。RETE演算法提供了專家系統的一個高效實現。
規則推理引擎做為產生式系統的一部分,當進行事實的斷言時,包含三個階段:匹配、選擇和執行,稱做match-select-act cycleRETE演算法可以對匹配階段進行高效實現,下面從鑑別網路和模式匹配過程兩個方面對該演算法進行介紹。

鑑別網路(如下圖所示):
RETE演算法在進行模式匹配時,是根據生成的鑑別網路來進行的。網路中非根結點的型別有1-input結點(也稱為alpha結點)和2-input結點(也稱為beta結點)兩種。1-input結點組成了Alpha網路,2-input結點組成了Beta網路。
每個非根結點都有一個儲存區。其中
1-input結點有alpha儲存區和一個輸入口;2-input結點有left儲存區和right儲存區和左右兩個輸入口,其中left儲存區是beta儲存區,right儲存區是alpha儲存區。儲存區儲存的最小單位是工作儲存區元素(Working Memory Element,簡稱WME),WME是為事實建立的元素,是用於和非根結點代表的模式進行匹配的元素。TokenWME的列表,包含有多個WME,(在Forgy的論文中,把Token看成是WME的列表或者單個WME,為了闡述方便,本文將把Token只看成WME的列表,該列表可以包含一個WME或者多個WME),用於2-input結點的左側輸入。事實可以做為2-input結點的右側輸入,也可以做為1-input結點的輸入。
每個非根結點都代表著產生式左部的一個模式,從根結點到終結點的路徑表示產生式的左部。

rete


規則匹配:
推理引擎在進行模式匹配時,先對事實進行斷言,為每一個事實建立WME,然後將WMERETE鑑別網路的根結點開始匹配,因為WME傳遞到的結點型別不同採取的演算法也不同,下面對alpha結點和beta結點處理WME的不同情況分開討論。

1)如果WME的型別和根節點的後繼結點TypeNodealpha結點的一種)所指定的型別相同,則會將該事實儲存在該TypeNode結點對應的alpha儲存區中,該WME被傳到後繼結點繼續匹配,否則會放棄該WME的後續匹配;

2)如果WME被傳遞到alpha結點,則會檢測WME是否和該結點對應的模式相匹配,若匹配,則會將該事實儲存在該alpha結點對應的儲存區中,該WME被傳遞到後繼結點繼續匹配,否則會放棄該WME的後續匹配;

3)如果WME被傳遞到beta結點的右端,則會加入到該beta結點的right儲存區,並和left儲存區中的Token進行匹配(匹配動作根據beta結點的型別進行,例如:joinprojectionselection),匹配成功,則會將該WME加入到Token中,然後將Token傳遞到下一個結點,否則會放棄該WME的後續匹配;

4)如果Token被傳遞到beta結點的左端,則會加入到該beta結點的left儲存區,並和right儲存區中的WME進行匹配(匹配動作根據beta結點的型別進行,例如:joinprojectionselection),匹配成功,則該Token會封裝匹配到的WME形成新的Token,傳遞到下一個結點,否則會放棄該Token的後續匹配;

5)如果WME被傳遞到beta結點的左端,將WME封裝成僅有一個WME元素的WME列表做為Token,然後按照(4)所示的方法進行匹配;

6)如果Token傳遞到終結點,則和該根結點對應的規則被啟用,建立相應的Activation,並存儲到Agenda當中,等待激發。

7)如果WME被傳遞到終結點,將WME封裝成僅有一個WME元素的WME列表做為Token,然後按照(6)所示的方法進行匹配;

以上是RETE演算法對於不同的結點,來進行WME或者token和結點對應模式的匹配的過程。

============================================上文提及資料的轉載===============================================

http://www.bstek.com
第 1  頁  共 74  頁
Drools5 Drools5 規則引擎 規則引擎
開發教程
高傑
上海銳道資訊科技有限公司
2009-8-20
http://www.bstek.com
第 2  頁  共 74  頁
1.  學習前的準備
Drools 是一款基於 Java 的開源規則引擎,所以在使用 Drools 之前需要在開發機器上安裝好
JDK 環境,Drools5 要求的 JDK 版本要在 1.5 或以上。
1.1.  開發環境搭建
大多數軟體學習的第一步就是搭建這個軟體的開發環境, Drools 也不例外。本小節的內
容就是介紹如何搭建一個 Drools5 的開發、執行、除錯環境。
1.1.1.  下載開發工具
 Drools5 提供了一個基於 Eclipse3.4 的一個 IDE 開發工具,所以在使用之前需要到
http://eclipse.org 網 站 下 載 一 個 3.4.x 版 本 的 Eclipse , 下 載 完 成 之 後 , 再 到
http://jboss.org/drools/downloads.html  網站,下載 Drools5 的 Eclipse 外掛版 IDE 及 Drools5
的開發工具包,如圖 1-1 所示。
http://www.bstek.com
第 3  頁  共 74  頁
圖 1-1
除這兩個下載包以外,還可以把 Drools5 的相關文件、原始碼和示例的包下載下來參考學
習使用。
將下載的開發工具包及 IDE 包解壓到一個非中文目錄下,解壓完成後就可以在
Eclipse3.4 上安裝 Drools5 提供的開發工具 IDE 了。
1.1.2.  安裝 Drools IDE
開啟 Eclipse3.4 所在目錄下的 links 目錄(如果該目錄不存在可以手工在其目錄下建立
一個 links 目錄), 在 links 目錄下建立一個文字檔案,並改名為 drools5-ide.link,用記事本打
開該檔案,按照下面的版本輸入 Drools5 Eclipse Plugin 檔案所在目錄:
path=D:\\eclipse\\drools-5.0-eclipse-all
這個值表示 Drools5  Eclipse  Plugin 檔案位於 D 盤 eclipse 目錄下的 drools-5.0-eclipse-all
下面,這裡有一點需要注意,那就是 drools-5.0-eclipse-all 資料夾下必須再包含一個 eclipse
目錄,所有的外掛檔案都應該位於該 eclipse 目錄之下,接下來要在 win dos 下重啟 Eclipse 3.4,
檢驗 Drools5 IDE 是否安裝成功。
進入 win dos,進入 Eclipes3.4 所在目錄,輸入 eclipse –clean 啟動 Eclipse3.4。啟動完成
http://www.bstek.com
第 4  頁  共 74  頁
後開啟選單 WindowPreferences,在彈出的視窗當中如果能在左邊導航樹中發現 Drools 節
點就表示 Drools5 IDE 安裝成功了,如圖 1-2 所示。
圖 1-2
 IDE 安裝完成後,接下來需要對 Drools5 的 IDE 環境進行簡單的配置,開啟選單
WindowPreferences ,在彈出的視窗當中選擇左邊導航樹選單 DroolsInstalled  Drools
Runtimes 設定 Drools5 IDE 執行時依賴的開發工具包,點選“Add…”按鈕新增一個開發工
具包,如圖 1-3 所示。
http://www.bstek.com
第 5  頁  共 74  頁
圖 1-3
圖 1-3 當中添加了一個開發工具包,名稱為“drools-5.0.0”,對應的工具包目錄為 D 盤
doc\about rule\drools5.x\drools-5.0-bin 目錄。新增完成後這樣 Drools5 的開發環境就搭好了。
下面我們就來利用這個環境編寫一個規則看看執行效果。
1.2.  編寫第一個規則
1.3.  規則的編譯與執行
在 Drools 當中,規則的編譯與執行要通過 Drools 提供的各種 API 來實現,這些 API 總
體來講可以分為三類:規則編譯、規則收集和規則的執行。完成這些工作的 API 主要有
http://www.bstek.com
第 6  頁  共 74  頁
KnowledgeBuilder、 KnowledgeBase、 StatefulKnowledgeSession、 StatelessKnowledgeSession、、
等,它們起到了對規則檔案進行收集、編譯、查錯、插入 fact、設定 global、執行規則或規
則流等作用,在正式接觸各種型別的規則檔案編寫方式及語法講解之前,我們有必要先熟悉
一下這些 API 的基本含義及使用方法。
1.3.1.  KnowledgeBuilder
規則編寫完成之後,接下來的工作就是在應用的程式碼當中呼叫這些規則,利用這些編寫
好的規則幫助我們處理業務問題。 KnowledgeBuilder 的作用就是用來在業務程式碼當中收集已
經編寫好的規則 ,然後對這些規則檔案進行編譯 ,最終產生一 批編譯好的規則包
(KnowledgePackage)給其它的應用程式使用。 KnowledgeBuilder 在編譯規則的時候可以通
過其提供的 hasErrors()方法得到編譯規則過程中發現規則是否有錯誤,如果有的話通過其提
供的 getErrors()方法將錯誤打印出來,以幫助我們找到規則當中的錯誤資訊。
建立 KnowledgeBuilder 物件使用的是 KnowledgeBuilderFactory 的 newKnowledgeBuilder
方法。程式碼清單 1-1 就演示了 KnowledgeBuilder 的用法。
程式碼清單 1-1
packagetest;
importjava.util.Collection;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.definition.KnowledgePackage;
importorg.drools.io.ResourceFactory;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder
kbuilder=KnowledgeBuilderFactory.newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("test.drl",
Test.class),ResourceType.DRL);
  Collection<KnowledgePackage>
kpackage=kbuilder.getKnowledgePackages();//產生規則包的集合
 }
}
http://www.bstek.com
第 7  頁  共 74  頁
通過 KnowledgeBuilder 編譯的規則檔案的型別可以有很多種,如.drl 檔案、.dslr 檔案或
一個 xls 檔案等。產生的規則包可以是具體的規則檔案形成的,也可以是規則流(rule flow)
檔案形成的,在新增規則檔案時,需要通過使用 ResourceType 的列舉值來指定規則檔案的
型別;同時在指定規則檔案的時候 drools 還提供了一個名為 ResourceFactory 的物件,通過
該物件可以實現從 Classpath、URL、File、ByteArray、Reader 或諸如 XLS 的二進位制檔案裡
新增載規則。
在規則檔案新增完成後,可以通過使用 hasErrors()方法來檢測已新增進去的規則當中有
沒有錯誤,如果不通過該方法檢測錯誤,那麼如果規則當中存在錯誤,最終在使用的時候也
會將錯誤丟擲。程式碼清單 1-2 就演示了通過 KnowledgeBuilder 來檢測規則當中有沒有錯誤。
程式碼清單 1-2
packagetest;
importjava.util.Collection;
importjava.util.Iterator;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderErrors;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.definition.KnowledgePackage;
importorg.drools.io.ResourceFactory;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder
kbuilder=KnowledgeBuilderFactory.newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("test.drl",
Test.class),ResourceType.DRL);
if(kbuilder.hasErrors()){
   System.out.println("規則中存在錯誤,錯誤訊息如下:");
   KnowledgeBuilderErrors kbuidlerErrors=kbuilder.getErrors();
for(Iterator
iter=kbuidlerErrors.iterator();iter.hasNext();){
    System.out.println(iter.next());
   }
  }
  Collection<KnowledgePackage>
kpackage=kbuilder.getKnowledgePackages();//產生規則包的集合
 }
http://www.bstek.com
第 8  頁  共 74  頁
}
後面隨著介紹的深入我們還會看到 KnowledgeBuilder 的一些其它用法。
1.3.2.  KnowledgeBase
 KnowledgeBase 是 Drools 提供的用來收集應用當中知識(knowledge)定義的知識庫對
象,在一個 KnowledgeBase 當中可以包含普通的規則(rule)、規則流(rule  flow)、函式定義
(function)、使用者自定義物件(type  model)等。KnowledgeBase 本身不包含任何業務資料對
象(fact 物件,後面有相應章節著重介紹 fact 物件), 業務物件都是插入到由 KnowledgeBase
產生的兩種型別的 session 物件當中 (StatefulKnowledgeSession 和 StatelessKnowledgeSession,
後面會有對應的章節對這兩種型別的物件進行介紹), 通過 session 物件可以觸發規則執行或
開始一個規則流執行。
建立一個 KnowledgeBase 要通過 KnowledgeBaseFactory 物件提供的 newKnowledgeBase
() 方法來實現,這其中建立的時候還可以為其指定一個 KnowledgeBaseConfiguration 物件,
KnowledgeBaseConfiguration 物件是一個用來存放規則引擎執行時相關環境引數定義的配置
物件,程式碼清單 1-3 演示了一個簡單的 KnowledgeBase 物件的建立過程。
程式碼清單 1-3
packagetest;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseFactory;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBase kbase=KnowledgeBaseFactory.newKnowledgeBase();
 }
}
代 碼 清 單 1-4 演 示 了 創 建 KnowledgeBase 過 程 當 中 , 使 用 一 個
KnowledgeBaseConfiguration 物件來設定環境引數。
程式碼清單 1-4
packagetest;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseConfiguration;
importorg.drools.KnowledgeBaseFactory;
http://www.bstek.com
第 9  頁  共 74  頁
public classTest {
public static voidmain(String[] args) {
  KnowledgeBaseConfiguration kbConf =
KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
  kbConf.setProperty( "org.drools.sequential", "true");
  KnowledgeBase kbase =
KnowledgeBaseFactory.newKnowledgeBase(kbConf);
 }
}
從程式碼清單 1-4 中可以看到,建立一個 KnowledgeBaseConfiguration 物件的方法也是使
用 KnowldegeBaseFactory,使用的是其提供的 newKnowledgeBaseConfiguration()方法,該方
法建立好的 KnowledgeBaseConfiguration 物件預設情況下會載入 drools-core-5.0.1.jar 包下
META-INF/drools.default.rulebase.conf 檔案裡的規則執行環境配置資訊,載入完成後,我們
可以在程式碼中對這些預設的資訊重新賦值,以覆蓋載入的預設值,比如這裡我們就把
org.drools.sequential 的值修改為 true,它的預設值為 false。
除了這種方式建立 KnowledgeBaseConfiguration 方法之外,我們還可以為其顯示的指定
一個 Properties 物件,在該物件中設定好需要覆蓋預設值的相關屬性的值,然後再通過
newKnowledgeBaseConfiguration(Properties  prop , ClassLoader  loader) 方 法 創 建 一 個
KnowledgeBaseConfiguration 物件。該方法方法當中第一個引數就是我們要設定的 Properties
物件,第二個引數用來設定載入 META-INF/drools.default.rulebase.conf 檔案的 ClassLoader,
因為該檔案在 ClassPath 下,所以採用的是 ClassLoader 方法進行載入,如果不指定這個引數,
那 麼 就 取 默 認 的 ClassLoader 對 象 , 如 果 兩 個 參 數 都 為 null , 那 麼 就 和
newKnowledgeBaseConfiguration()方法的作用相同了,程式碼清單程式碼清單 1-5 演示了這種用
法。
程式碼清單 1-5
packagetest;
importjava.util.Properties;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseConfiguration;
importorg.drools.KnowledgeBaseFactory;
public classTest {
public static voidmain(String[] args) {
http://www.bstek.com
第 10  頁  共 74  頁
  Properties properties = newProperties();
  properties.setProperty( "org.drools.sequential", "true");
  KnowledgeBaseConfiguration kbConf =
KnowledgeBaseFactory.newKnowledgeBaseConfiguration(properties, null);
  KnowledgeBase kbase =
KnowledgeBaseFactory.newKnowledgeBase(kbConf);
 }
}
用來設定預設規則執行環境檔案 drools.default.rulebase.conf 裡面所涉及到的具體項內容
如程式碼清單 1-6 所示:
程式碼清單 1-6
drools.maintainTms = <true|false>
drools.assertBehaviour = <identity|equality>
drools.logicalOverride = <discard|preserve>
drools.sequential = <true|false>
drools.sequential.agenda = <sequential|dynamic>
drools.removeIdentities = <true|false>
drools.shareAlphaNodes = <true|false>
drools.shareBetaNodes = <true|false>
drools.alphaNodeHashingThreshold = <1...n>
drools.compositeKeyDepth = <1..3>
drools.indexLeftBetaMemory = <true/false>
drools.indexRightBetaMemory = <true/false>
drools.consequenceExceptionHandler = <qualified class name>
drools.maxThreads = <-1|1..n>
drools.multithreadEvaluation = <true|false>
在後面的內容講解過程當中,對於程式碼清單 1-6 裡列出的屬性會逐個涉及到,這裡就不
再多講了。
 KnowledgeBase 建立完成之後,接下來就可以將我們前面使用 KnowledgeBuilder 生成的
KnowledgePackage 的集合新增到 KnowledgeBase 當中,以備使用,如程式碼清單 1-7 所示。
程式碼清單 1-7
packagetest;
importjava.util.Collection;
http://www.bstek.com
第 11  頁  共 74  頁
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseConfiguration;
importorg.drools.KnowledgeBaseFactory;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.definition.KnowledgePackage;
importorg.drools.io.ResourceFactory;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
    .newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("test.drl",
    Test.class), ResourceType.DRL);
  Collection<KnowledgePackage> kpackage =
kbuilder.getKnowledgePackages();
  KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory
    .newKnowledgeBaseConfiguration();
  kbConf.setProperty("org.drools.sequential", "true");
  KnowledgeBase kbase =
KnowledgeBaseFactory.newKnowledgeBase(kbConf);
  kbase.addKnowledgePackages(kpackage);//將KnowledgePackage集合添
加到KnowledgeBase當中
 }
}
1.3.3.  StatefulKnowledgeSession
規則編譯完成之後,接下來就需要使用一個 API 使編譯好的規則包檔案在規則引擎當
中執行起來。在 Drools5 當中提供了兩個物件與規則引擎進行互動: StatefulKnowledgeSession
和 StatelessKnowledgeSession,本小節當中要介紹的是 StatefulKnowledgeSession 物件,下面
的一節將對 StatelessKnowledgeSession 物件進行討論。
 StatefulKnowledgeSession 物件是一種最常用的與規則引擎進行互動的方式,它可以與規
則引擎建立一個持續的互動通道,在推理計算的過程當中可能會多次觸發同一資料集。在用
戶的程式碼當中,最後使用完 StatefulKnowledgeSession 物件之後,一定要呼叫其 dispose()方
http://www.bstek.com
第 12  頁  共 74  頁
法以釋放相關記憶體資源。
 StatefulKnowledgeSession 可以接受外部插入(insert)的業務資料——也叫 fact,一個
fact 物件通常是一個普通的 Java 的 POJO,一般它們會有若干個屬性,每一個屬性都會對應
getter 和 setter 方法,用來對外提供資料的設定與訪問。一般來說,在 Drools 規則引擎當中,
fact 所承擔的作用就是將規則當中要用到的業務資料從應用當中傳入進來,對於規則當中產
生的資料及狀態的變化通常不用 fact 傳出。如果在規則當中需要有資料傳出,那麼可以通過
在 StatefulKnowledgeSession 當中設定 global 物件來實現,一個 global 物件也是一個普通的
Java 物件,在向 StatefulKnowledgeSession 當中設定 global 物件時不用 insert 方法而用
setGlobal 方法實現。
建立一個 StatefulKnowledgeSession 要通過 KnowledgeBase 物件來實現,下面的程式碼清
單 1-8 就演示了 StatefulKnowledgeSession 的用法:
程式碼清單 1-8
packagetest;
importjava.util.Collection;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseConfiguration;
importorg.drools.KnowledgeBaseFactory;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.definition.KnowledgePackage;
importorg.drools.io.ResourceFactory;
importorg.drools.runtime.StatefulKnowledgeSession;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
    .newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("test.drl",
    Test.class), ResourceType.DRL);
  Collection<KnowledgePackage> kpackage =
kbuilder.getKnowledgePackages();
  KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory
    .newKnowledgeBaseConfiguration();
  kbConf.setProperty("org.drools.sequential", "true");
http://www.bstek.com
第 13  頁  共 74  頁
  KnowledgeBase kbase =
KnowledgeBaseFactory.newKnowledgeBase(kbConf);
  kbase.addKnowledgePackages(kpackage);//將KnowledgePackage集合添
加到KnowledgeBase當中
  StatefulKnowledgeSession
statefulKSession=kbase.newStatefulKnowledgeSession();
  statefulKSession.setGlobal("globalTest", newObject());//設定一
個global物件
  statefulKSession.insert(newObject());//插入一個fact物件
  statefulKSession.fireAllRules();
  statefulKSession.dispose();
 }
}
程式碼清單 1-8 當中同時也演示了規則完整的執行處理過程,可以看到,它的過程是首先
需要通過使用 KnowledgeBuilder 將相關的規則檔案進行編譯,產生對應的 KnowledgePackage
集合,接下來再通過 KnowledgeBase 把產生的 KnowledgePackage 集合收集起來,最後再產
生 StatefulKnowledgeSession 將規則當中需要使用的 fact 物件插入進去、將規則當中需要用
到的 global 設定進去,然後呼叫 fireAllRules()方法觸發所有的規則執行,最後呼叫 dispose()
方法將記憶體資源釋放。
1.3.4.  StatelessKnowledgeSession
 StatelessKnowledgeSession 的作用與 StatefulKnowledgeSession 相仿,它們都是用來接收
業務資料、執行規則的。事實上,StatelessKnowledgeSession 對 StatefulKnowledgeSession 做
了包裝,使得在使用 StatelessKnowledgeSession 物件時不需要再呼叫 dispose()方法釋放記憶體
資源了。
因為 StatelessKnowledgeSession 本身所具有的一些特性,決定了它的使用有一定的侷限
性。在使用 StatelessKnowledgeSession 時不能進行重複插入 fact 的操作、也不能重複的呼叫
fireAllRules()方法來執行所有的規則,對應這些要完成的工作在 StatelessKnowledgeSession
當中只有 execute(…)方法,通過這個方法可以實現插入所有的 fact 並且可以同時執行所有的
規則或規則流,事實上也就是在執行 execute(…)方法的時候就在 StatelessKnowledgeSession
內部執行了 insert()方法、fireAllRules()方法和 dispose()方法。
程式碼清單 1-9 演示了 StatelessKnowledgeSession 物件的用法。
http://www.bstek.com
第 14  頁  共 74  頁
程式碼清單 1-9
packagetest;
importjava.util.ArrayList;
importjava.util.Collection;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseConfiguration;
importorg.drools.KnowledgeBaseFactory;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.definition.KnowledgePackage;
importorg.drools.io.ResourceFactory;
importorg.drools.runtime.StatelessKnowledgeSession;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
    .newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("test.drl",
    Test.class), ResourceType.DRL);
  Collection<KnowledgePackage> kpackage =
kbuilder.getKnowledgePackages();
  KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory
    .newKnowledgeBaseConfiguration();
  kbConf.setProperty("org.drools.sequential", "true");
  KnowledgeBase kbase =
KnowledgeBaseFactory.newKnowledgeBase(kbConf);
  kbase.addKnowledgePackages(kpackage);//將KnowledgePackage集合添
加到KnowledgeBase當中
  StatelessKnowledgeSession
statelessKSession=kbase.newStatelessKnowledgeSession();
  ArrayList list=newArrayList();
  list.add(newObject());
  list.add(newObject());
  statelessKSession.execute(list);
 }
}
代 碼 清 單 1-9 當 中 , 通 過 新 建 了 一 個 ArrayList 對 象 , 將 需 要 插 入 到
http://www.bstek.com
第 15  頁  共 74  頁
StatelessKnowledgeSession 當中的物件放到這個 ArrayList 當中,將這個 ArrayList 作為引數
傳給 execute(…)方法,這樣在 StatelessKnowledgeSession 內部會對這個 ArrayList 進行迭代,
取出其中的每一個 Element,將其作為 fact,呼叫 StatelessKnowledgeSession 物件內部的
StatefulKnowledgeSession 對 象 的 insert() 方 法 將 產 生 的 fact 逐 個 插 入 到
StatefulKnowledgeSession 當中,然後呼叫 StatefulKnowledgeSession 的 fireAllRules()方法,
最後執行 dispose()方法釋放記憶體資源。
在程式碼清單 1-9 當中,如果我們要插入的 fact 就是這個 ArrayList 而不是它內部的 Element
那該怎麼做呢?在 StatelessKnowledgeSession 當中,還提供了 execute(Command cmd)的方法,
在該方法中通過 CommandFactory 可以建立各種型別的 Command,比如前面的需求要直接
將這個 ArrayList 作為一個 fact 插入,那麼就可以採用 CommandFactory.newInsert(Object obj)
來實現,程式碼清單 1-9 當中 execute 方法可做如程式碼清單 1-10 所示的修改。
程式碼清單 1-10
statelessKSession.execute(CommandFactory.newInsert(list));
如 果 需 要 通 過 StatelessKnowledgeSession 設 置 global 的 話 , 可 以 使 用
CommandFactory.newSetGlobal(“key”,Object  obj)來實現;如果即要插入若干個 fact,又要設
置相關的 global,那麼可以將 CommandFactory 產生的 Command 物件放在一個 Collection 當
中,然後再通過 CommandFactory.newBatchExecution(Collection collection)方法實現。程式碼清
單 1-11 演示了這種做法。
程式碼清單 1-11
packagetest;
importjava.util.ArrayList;
importjava.util.Collection;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseConfiguration;
importorg.drools.KnowledgeBaseFactory;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.command.Command;
importorg.drools.command.CommandFactory;
importorg.drools.definition.KnowledgePackage;
importorg.drools.io.ResourceFactory;
importorg.drools.runtime.StatelessKnowledgeSession;
http://www.bstek.com
第 16  頁  共 74  頁
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
    .newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("test.drl",
    Test.class), ResourceType.DRL);
  Collection<KnowledgePackage> kpackage =
kbuilder.getKnowledgePackages();
  KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory
    .newKnowledgeBaseConfiguration();
  kbConf.setProperty("org.drools.sequential", "true");
  KnowledgeBase kbase =
KnowledgeBaseFactory.newKnowledgeBase(kbConf);
  kbase.addKnowledgePackages(kpackage);//將KnowledgePackage集合添
加到KnowledgeBase當中
  StatelessKnowledgeSession
statelessKSession=kbase.newStatelessKnowledgeSession();
  ArrayList<Command> list=newArrayList<Command>();
  list.add(CommandFactory.newInsert(newObject()));
  list.add(CommandFactory.newInsert(newObject()));
  list.add(CommandFactory.newSetGlobal("key1", newObject()));
  list.add(CommandFactory.newSetGlobal("key2", newObject()));
 statelessKSession.execute(CommandFactory.newBatchExecution(list))
;
 }
}
1.4. Fact 物件
Fact 是指在 Drools 規則應用當中,將一個普通的 JavaBean 插入到規則的 WorkingMemory
當中後的物件。規則可以對 Fact 物件進行任意的讀寫操作,當一個 JavaBean 插入到
WorkingMemory 當中變成 Fact 之後, Fact 物件不是對原來的 JavaBean 物件進行 Clon,而是
原來 JavaBean 物件的引用。規則在進行計算的時候需要用到應用系統當中的資料,這些數
據設定在 Fact 物件當中,然後將其插入到規則的 WorkingMemory 當中,這樣在規則當中就
可以通過對 Fact 物件資料的讀寫,從而實現對應用資料的讀寫操作。一個 Fact 物件通常是
http://www.bstek.com
第 17  頁  共 74  頁
一個具有 getter 和 setter 方法的 POJO 物件,通過這些 getter 和 setter 方法可以方便的實現對
Fact 物件的讀寫操作,所以我們可以簡單的把 Fact 物件理解為規則與應用系統資料互動的
橋樑或通道。
當 Fact 物件插入到 WorkingMemory 當中後,會與當前 WorkingMemory 當中所有的規
則進行匹配,同時返回一個 FactHandler 物件。FactHandler 物件是插入到 WorkingMemory
當中 Fact 物件的引用控制代碼,通過 FactHandler 物件可以實現對對應的 Fact 物件的刪除及修改
等操作。
在前面介紹 StatefulKnowledgeSession 和 StatelessKnowledgeSession 兩個物件的時候也提
到了插入 Fact 物件的方法,在 StatefulKnowledgeSession 當中直接使用 insert 方法就可以將
一個 Java 物件插入到 WokingMemory 當中,如果有多個 Fact 需要插入,那麼多個呼叫 insert
方法即可;對於 StatelessKnowledgeSession 物件可利用 CommandFactory 實現單個 Fact 物件
或多個 Fact 物件的插入。
http://www.bstek.com
第 18  頁  共 74  頁
2.  規則
學習 Drools 規則語法的目的是為了在應用當中幫助我們解決實際的問題,所以學會並
靈活的在規則當中使用就顯的尤為重要。本章的內容包括規則的基本的約束部分語法講解
(LHS)、規則動作執行部分語法講解及規則的各種屬性介紹。
2.1.  規則檔案
在 Drools 當中,一個標準的規則檔案就是一個以“.drl”結尾的文字檔案,由於它是一
個標準的文字檔案,所以可以通過一些記事本工具對其進行開啟、檢視和編輯。規則是放在
規則檔案當中的,一個規則檔案可以存放多個規則,除此之外,在規則檔案當中還可以存放
使用者自定義的函式、資料物件及自定義查詢等相關在規則當中可能會用到的一些物件。
一個標準的規則檔案的結構如程式碼清單 2-1 所示。
程式碼清單 2-1
package  package package-name
imports
globals
functions
queries
rules
對於一個規則檔案而言,首先宣告 package 是必須的,除 package 之外,其它物件在規
則檔案中的順序是任意的,也就是說在規則檔案當中必須要有一個 package 宣告,同時
package 宣告必須要放在規則檔案的第一行。
規則檔案當中的 package 和 Java 語言當中的 package 有相似之處,也有不同之處。在 Java
當中 package 的作用是用來對功能相似或相關的檔案放在同一個 package 下進行管理,這種
package 管理既有物理上 Java 檔案位置的管理也有邏輯上的檔案位置的管理,在 Java 當中
這種通過 package 管理檔案要求在檔案位置在邏輯上與物理上要保持一致;在 Drools 的規則
http://www.bstek.com
第 19  頁  共 74  頁
檔案當中 package 對於規則檔案中規則的管理只限於邏輯上的管理,而不管其在物理上的位
置如何,這點是規則與 Java 檔案的 package 的區別。
對於同一 package 下的使用者自定義函式、自定義的查詢等,不管這些函式與查詢是否在
同一個規則檔案裡面,在規則裡面是可以直接使用的,這點和 Java 的同一 package 裡的 Java
類呼叫是一樣的。
2.2.  規則語言
規則是在規則檔案當中編寫,所以要編寫一個規則首先需要先建立一個存放規則的規則
檔案。一個規則檔案可以存放若干個規則,每一個規則通過規則名稱來進行標識。程式碼清單
2-2 說明了一個標準規則的結構。
程式碼清單 2-2
rule rule "name"
attributes
when when
LHS
then then
RHS
end endend end
從程式碼清單 2-2 中可以看到,一個規則通常包括三個部分:屬性部分(attribute)、條件
部分(LHS)和結果部分(RHS)。對於一個完整的規則來說,這三個部分都是可選的,也
就是說如程式碼清單 2-3 所示的規則是合法的。
程式碼清單 2-3
rule rule "name"
when when
then then
end
對於程式碼清單 2-3 所示的這種規則,因為其沒有條件部分,預設它的條件部分是滿足的,
因為其結果執行部分為空,所以即使條件部分滿足該規則也什麼都不做。接下來我們就分別
來對規則的條件部分、結果部分和屬性部分進行介紹。
2.2.1.  條件部分
條件部分又被稱之為 Left Hand Side,簡稱為 LHS,下文當中,如果沒有特別指出,那
麼所說的 LHS 均指規則的條件部分,在一個規則當中 when 與 then 中間的部分就是 LHS 部
http://www.bstek.com
第 20  頁  共 74  頁
分。在 LHS 當中,可以包含 0~n 個條件,如果 LHS 部分沒空的話,那麼引擎會自動新增一
個 eval(true)的條件,由於該條件總是返回 true,所以 LHS 為空的規則總是返回 true。所以
程式碼清單 2-3 所示的規則在執行的時候引擎會修改成如程式碼清單 2-4 所示的內容。
程式碼清單 2-4
rule rule "name"
when when
eval(true)
then then
end
 LHS 部分是由一個或多個條件組成,條件又稱之為 pattern(匹配模式),多個 pattern
之間用可以使用 and 或 or 來進行連線,同時還可以使用小括號來確定 pattern 的優先順序。
一個 pattern 的語法如程式碼清單 2-5 所示:
程式碼清單 2-5
[繫結變數名:]Object([field 約束])
對於一個 pattern 來說“繫結變數名”是可選的,如果在當前規則的 LHS 部分的其它的
pattern 要用到這個物件,那麼可以通過為該物件設定一個繫結變數名來實現對其引用,對於
繫結變數的命名,通常的作法是為其新增一個“$”符號作為字首,這樣可以很好的與 Fact
的屬性區別開來;繫結變數不僅可以用在物件上,也可以用在物件的屬性上面,命名方法與
物件的命名方法相同;“field 約束”是指當前物件裡相關欄位的條件限制,程式碼清單 2-6 規
則中 LHS 部分單個 pattern 的情形。
程式碼清單 2-6
rule rule "rule1"
when when
$customer:Customer()
then then
<action>…
end
程式碼清單 2-6 規則中“$customer”是就是一個繫結到 Customer 物件的“繫結變數名”,
該規則的 LHS 部分表示,要求 Fact 物件必須是 Customer 型別,該條件滿足了那麼它的 LHS
會返回 true。
程式碼清單 2-7 演示了多個約束構成的 LHS 部分。
程式碼清單 2-7
rule rule "rule1"
when when
http://www.bstek.com
第 21  頁  共 74  頁
$customer:Customer(age>20,gender==’male’)
Order(customer==$customer,price>1000)
then then
<action>…
end
程式碼清單 2-7 中的規則就包含兩個 pattern,第一個 pattern 有三個約束,分別是:物件
型別必須是 Cutomer;同時 Cutomer 的 age 要大於 20 且 gender 要是 male;第二個 pattern 也
有三個約束,分別是:物件型別必須是 Order,同時 Order 對應的 Cutomer 必須是前面的那
個 Customer 且當前這個 Order 的 price 要大於 1000。在這兩個 pattern 沒有符號連線,在 Drools
當中在 pattern 中沒有連線符號,那麼就用 and 來作為預設連線,所以在該規則的 LHS 部分
中兩個 pattern 只有都滿足了才會返回 true。預設情況下,每行可以用 “;”來作為結束符 (和
Java 的結束一樣),當然行尾也可以不加“;”結尾。
2.2.1.1.  約束連線
對於物件內部的多個約束的連線,可以採用 “&&”(and)、“||”(or)和“,”(and)來實現,
程式碼清單 2-7 中規則的 LHS 部分的兩個 pattern 就裡物件內部約束就採用 “,”來實現,“&&”
(and)、 “||”(or)和“,”這三個連線符號如果沒有用小括號來顯示的定義優先順序的話,那麼
它們的執行順序是:“&&”(and)、 “||”(or)和“,”。程式碼清單 2-8 演示了使用“&&”(and)、
“||”(or)來連線約束的情形。
程式碼清單 2-8
rule rule "rule1"
when when
Customer(age>20 || gender==’male’&& city==’sh’)
then then
<action>…
End
程式碼清單 2-8 中規則的 LHS 部分只有一個 pattern,在這個 pattern 中有四個約束,首先
必須是一個 Customer 物件,然後要麼該物件 gender=’male’且 city=’sh’,要麼 age>20,在
Customer 物件的欄位約束當中,age>20 和 gender=’male’且 city=’sh’這兩個有一個滿足就可
以了,這是因為“&&”連線符的優先順序要高於“||”,所以程式碼清單 2-8 中規則的 LHS 部分
pattern 也可以寫成程式碼清單 2-9 的樣子,用一小括號括起來,這樣看起來就更加直觀了。
程式碼清單 2-9
rule rule "rule1"
http://www.bstek.com
第 22  頁  共 74  頁
when when
Customer(age>20 || (gender==’male’&& city==’sh’))
then then
<action>…
End
表面上看“,”與“&&”具有相同的含義,但是有一點需要注意,“,”與“&&”和“||”
不能混合使用,也就是說在有“&&”或“||”出現的 LHS 當中,是不可以有“,”連線符出
現的,反之亦然。
2.2.1.2.  比較操作符
在前面規則例子當中,我們已經接觸到了諸如 “>”、“= =”之類的比較操作符,在 Drools5
當中共提供了十二種型別的比較操作符,分別是: >、 >=、 <、 <=、 = =、 !=、 contains、 not contains、
memberof、not memberof、matches、not matches;在這十二種型別的比較操作符當中,前六
個是比較常見也是用的比較多的比較操作符,本小節當中將著重對後六種型別的比較操作符
進行介紹。
2.2.1.2.1.  contains
比較操作符 contains 是用來檢查一個 Fact 物件的某個欄位(該欄位要是一個 Collection
或是一個 Array 型別的物件)是否包含一個指定的物件。程式碼清單 2-10 演示了 contains 比較
操作符的用法。
程式碼清單 2-10
#created on: 2009-8-26
packagetest
rule"rule1"
when
  $order:Order();
  $customer:Customer(age >20, orders contains$order);
then
  System.out.println($customer.getName());
end
 contains 操作符的語法如下:
http://www.bstek.com
第 23  頁  共 74  頁
Object(field[Collection/Array] contains value)
 contains 只能用於物件的某個 Collection/Array 型別的欄位與另外一個值進行比較,作為
比較的值可以是一個靜態的值,也可以是一個變數(繫結變數或者是一個 global 物件),在代
碼清單 2-10 當中比較值$order 就是一個繫結變數。
2.2.1.2.2.  not contains
not  contains 作用與 contains 作用相反,not  contains 是用來判斷一個 Fact 物件的某個字
段(Collection/Array 型別)是不是包含一個指定的物件,和 contains 比較符相同,它也只能
用在物件的 field 當中,程式碼清單 2-11 演示了 not contains 用法。
程式碼清單 2-11
#created on: 2009-8-26
packagetest
rule"rule1"
when
  $order:Order(items not contains "手機");
then
  System.out.println($order.getName());
end
程式碼清單 2-11 的規則當中,在判斷訂單 (Order)的時候,要求訂單當中不能包含有 “手
機”的貨物,這裡的規則對於比較的項就是一個表態固定字串。
2.2.1.2.3.  memberOf
memberOf 是用來判斷某個 Fact 物件的某個欄位是否在一個集合(Collection/Array)當
中,用法與 contains 有些類似,但也有不同,memberOf 的語法如下:
Object(fieldName memberOf value[Collection/Array])
可以看到 memberOf 中集合型別的資料是作為被比較項的,集合型別的資料物件位於
memberOf 操作符後面,同時在用 memberOf 比較操作符時被比較項一定要是一個變數(繫結
變數或者是一個 global 物件),而不能是一個靜態值。程式碼清單 2-12 是一個演示 memberOf
使用的規則示例。
http://www.bstek.com
第 24  頁  共 74  頁
程式碼清單 2-12
#created on: 2009-8-26
packagetest
globalString[] orderNames;
rule"rule1"
when
  $order:Order(name memberOforderNames);
then
  System.out.println($order.getName());
end
程式碼清單 2-12 中被比較物件是一個 String Array 型別的 global 物件。
2.2.1.2.4.  not memberOf
該操作符與 memberOf 作用洽洽相反,是用來判斷 Fact 物件當中某個欄位值是不是中
某個集合 (Collection/Array)當中,同時被比較的集合物件只能是一個變數 (繫結變數或 global
物件),程式碼清單 2-13 演示了 not memberOf 的用法。
程式碼清單 2-13
#created on: 2009-8-26
packagetest
importjava.util.List;
rule"rule1"
when
  $orderList:String[]();
  $order:Order(name not memberOf$orderList);
then
  System.out.println($order.getName());
end
程式碼清單 2-13 中表示只有訂單(Order)的名字(name)在訂單集合($orderList)時
LHS 才能返回 true,該例子當中被比較的集合$orderList 是一個字串陣列的型別的繫結
變數物件。
2.2.1.2.5.  matches
matches 是用來對某個 Fact 的欄位與標準的 Java 正則表示式進行相似匹配,被比較的字
http://www.bstek.com
第 25  頁  共 74  頁
符串可以是一個標準的 Java 正則表示式,但有一點需要注意,那就是正則表示式字串當
中不用考慮“\”的轉義問題。matches 使用語法如下:
程式碼清單 2-14 演示了 matches 的用法
程式碼清單 2-14
#created on: 2009-8-26
packagetest
importjava.util.List;
rule "rule1"
when
  $customer:Customer(name matches "李.*");
then
  System.out.println($customer.getName());
end
在清單程式碼清單 2-14 中示例的規則就像我們展示了 matches 的用法,該規則是用來查
找所有 Customer 物件的 name 屬性是不是以“李”字開頭,如果滿足這一條件那麼就將該
Customer 物件的 name 屬性打印出來,程式碼清單 2-15 是對該規則的測試類原始碼。
程式碼清單 2-15
packagetest;
importjava.util.Collection;
importorg.drools.KnowledgeBase;
importorg.drools.KnowledgeBaseFactory;
importorg.drools.builder.KnowledgeBuilder;
importorg.drools.builder.KnowledgeBuilderFactory;
importorg.drools.builder.ResourceType;
importorg.drools.io.impl.ClassPathResource;
importorg.drools.runtime.StatefulKnowledgeSession;
public classTest {
public static voidmain(String[] args) {
  KnowledgeBuilder
kb=KnowledgeBuilderFactory.newKnowledgeBuilder();
  kb.add(newClassPathResource("test/test.drl"),
ResourceType.DRL);
  Collection collection=kb.getKnowledgePackages();
  KnowledgeBase
knowledgeBase=KnowledgeBaseFactory.newKnowledgeBase();
Object(fieldName matches “正則表示式”)
http://www.bstek.com
第 26  頁  共 74  頁
  knowledgeBase.addKnowledgePackages(collection);
  StatefulKnowledgeSession
statefulSession=knowledgeBase.newStatefulKnowledgeSession();
  Customer cus1=newCustomer();
  cus1.setName("張三");
  Customer cus2=newCustomer();
  cus2.setName("李四");
  Customer cus3=newCustomer();
  cus3.setName("王二");
  Customer cus4=newCustomer();
  cus4.setName("李小龍");
  statefulSession.insert(cus1);
  statefulSession.insert(cus2);
  statefulSession.insert(cus3);
  statefulSession.insert(cus4);
  statefulSession.fireAllRules();
  statefulSession.dispose();
  System.out.println("end.....");
 }
}
測試類執行結果如圖 2-1 所示。
圖 2-1
從測試結果中可以看到,該規則運用 matches 操作符已經將所有的以“李”字開頭的
Customer 物件找到並打印出來。
2.2.1.2.6.  not matches
與 matches 作用相反,是用來將某個 Fact 的欄位與一個 Java 標準正則表示式進行匹配,
看是不是能與正則表示式匹配。not matches 使用語法如下:
因為 not  matches 用法與 matches 一樣,只是作用相反,所以這裡就不再舉例了,有興
Object(fieldname not matches “正則表示式”)
http://www.bstek.com
第 27  頁  共 74  頁
趣的讀者可以用 not matches 寫個例子測試一下。
2.2.2.  結果部分
條件部分又被稱之為 Right Hand Side,簡稱為 RHS,在一個規則當中 then 後面部分就
是 RHS,只有在 LHS 的所有條件都滿足時 RHS 部分才會執行。
 RHS 部分是規則真正要做事情的部分,可以將因條件滿足而要觸發的動作寫在該部分
當中,在 RHS 當中可以使用 LHS 部分當中定義的繫結變數名、設定的全域性變數、或者是直
接編寫 Java 程式碼(對於要用到的 Java 類,需要在規則檔案當中用 import 將類匯入後方能使
用,這點和 Java 檔案的編寫規則相同)。
我們知道,在規則當中 LHS 就是用來放置條件的,所以在 RHS 當中雖然可以直接編寫
Java 程式碼,但不建議在程式碼當中有條件判斷,如果需要條件判斷,那麼請重新考慮將其放在
LHS 當中,否則就違背了使用規則的初衷。
在 Drools 當中,在 RHS 裡面,提供了一些對當前 Working  Memory 實現快速操作的巨集
巨集函式或物件,比如 insert/insertLogical、 update 和 retract 就可以實現對當前 Working Memory
中的 Fact 物件進行新增、刪除或者是修改;如果您覺得還要使用 Drools 當中提供的其它方
法,那麼您還可以使用另一外巨集物件 drools,通過該物件可以使用更多的操作當前 Working
Memory 的方法;同時 Drools 還提供了一個名為 kcontext 的巨集物件,使我們可以通過該物件
直接訪問當前 Working Memory 的 KnowledgeRuntime。下面我們就來詳細討論一下這些巨集函
數的用法。
2.2.2.1.  insert
函式 insert 的作用與我們在 Java 類當中呼叫 StatefulKnowledgeSession 物件的 insert 方法
的作用相同,都是用來將一個 Fact 物件插入到當前的 Working Memory 當中。它的基本用法
格式如下:
insert(new Object());
一旦呼叫 insert 巨集函式,那麼 Drools 會重新與所有的規則再重新匹配一次,對於沒有設
置 no-loop 屬性為 true 的規則,如果條件滿足,不管其之前是否執行過都會再執行一次,這
個特性不僅存在於 insert 巨集函式上,後面介紹的 update、retract 巨集函式同樣具有該特性,所
http://www.bstek.com
第 28  頁  共 74  頁
以在某些情況下因考慮不周呼叫 insert、update 或 retract 容易發生死迴圈,這點大家需要注
意。
程式碼清單 2-16 演示了 insert 函式的用法。
程式碼清單 2-16
#created on: 2009-8-26
packagetest
importjava.util.List;
rule "rule1"
salience1
when
eval(true);
then
  Customer cus=newCustomer();
  cus.setName("張三");
insert(cus);
end
rule "rule2"
salience2
when
  $customer:Customer(name =="張三");
then
  System.out.println("rule2----"+$customer.getName());
end
程式碼清單 2-16 中有兩個規則 rule1 和 rule2,在 rule1 這個規則當中,LHS 是 eval(true),
表示沒有任何條件限制(關於 eval 的用法,在後續的內容當中會有詳細的介紹), RHS 當中
建立了一個 Customer 物件,並設定它的 name 屬性為“張三”,完成後呼叫 insert 巨集函式將
其插入到當前的 Working Memory 當中;在名為 rule2 這個規則當中,首先判斷當前的 Working
Memory 當中沒有一個 name 屬性的值為“張三”