Drools 7.4.1.Final参考手册(六) 用户手册
用戶手冊(cè)
基礎(chǔ)
無狀態(tài)的知識(shí)Session
Drools規(guī)則引擎擁有大量的用例和功能,我們要如何開始?你無須擔(dān)心,這些復(fù)雜性是分層的,你可以用簡單的用例來逐步入門。
無狀態(tài)Session,無須使用推理,就形成了最簡單的用例。一個(gè)無狀態(tài)session可以經(jīng)過一個(gè)函數(shù),然后返回一些結(jié)果。無狀態(tài)session的用例都具有但不限于如下功能:
-
校驗(yàn)
-
此人是否有資格申請(qǐng)抵押貸款
-
-
計(jì)算
-
計(jì)算抵押溢價(jià)
-
-
路由和過濾
-
將傳入的郵件(如電子郵件)過濾到文件夾中
-
將傳入的消息發(fā)送到目的地
-
讓我們從一個(gè)簡單的例子開始,使用一個(gè)駕駛執(zhí)照申請(qǐng)。
public class Applicant {private String name; private int age; private boolean valid; // getter and setter methods here }根據(jù)上面的數(shù)據(jù)模型,我們?yōu)樗砑右粋€(gè)規(guī)則:18歲以下的申請(qǐng)人會(huì)被拒絕。
package com.company.licenserule "Is of valid age" when$a : Applicant( age < 18 ) then$a.setValid( false ); end為了使規(guī)則引擎能夠識(shí)別數(shù)據(jù),從而能夠針對(duì)一些規(guī)則進(jìn)行處理,我們需要?插入?數(shù)據(jù),就像一個(gè)數(shù)據(jù)庫一樣。當(dāng)Applicant實(shí)例被插入到引擎時(shí),首先會(huì)校驗(yàn)約束條件,在這個(gè)用例中只有兩個(gè)約束條件。之所以說是兩個(gè)規(guī)則,其一是Applicant的類型校驗(yàn),其二是age < 18的規(guī)則校驗(yàn)。一個(gè)對(duì)象類型加上它的另個(gè)或多個(gè)字段約束稱為模式。當(dāng)插入的實(shí)例同時(shí)滿足這兩個(gè)約束時(shí),那么認(rèn)為實(shí)例能夠匹配這一規(guī)則。
$a是一個(gè)綁定變量,它允許我們引用匹配約束的對(duì)象實(shí)例。這一實(shí)例的屬性在then后面會(huì)被更新,('$')符號(hào)是可選的,但它能夠幫助用來從不同的字段名中區(qū)分不同的變量名。僅僅處理被插入的數(shù)據(jù)中能夠匹配模式的數(shù)據(jù)的這種做法,我們稱為模式匹配。
在Drools文件中推入規(guī)則是十分必要的,它只是一個(gè)普通的文本文件,只是后綴名是 .drl,它是 "Drools Rule Language" 的簡稱。那么讓我們來調(diào)用一下文件 licenseApplication.drl ,然后在Kie Project中保存。 Kie Project具有一個(gè)正常的Maven項(xiàng)目結(jié)構(gòu),還一個(gè)附加的定義了KieBases 和?KieSession?的 kmodule.xml文件。這個(gè)文件必須放在Maven項(xiàng)目的resources / META-INF文件夾中,而包含前一個(gè)規(guī)則的所有其他Drools構(gòu)件(如licenseApplication.drl)必須存儲(chǔ)在資源文件夾或其下的任何其他子文件夾中。
由于所有配置方面都提供了有意義的默認(rèn)值,所以最簡單的kmodule.xml文件只需包含一個(gè)空的kmodule標(biāo)簽,如下所示:
<?xml version="1.0" encoding="UTF-8"?> <kmodule xmlns="http://www.drools.org/xsd/kmodule"/>在這一點(diǎn)上,可以創(chuàng)建一個(gè)KieContainer來從類路徑中讀取要生成的文件。
KieServices kieServices = KieServices.Factory.get(); KieContainer kContainer = kieServices.getKieClasspathContainer();上面的代碼片段編譯了在classpath中找到的所有DRL文件,并把這個(gè)編譯的結(jié)果,一個(gè)KieModule,放到KieContainer中。 如果沒有錯(cuò)誤,我們將準(zhǔn)備從“KieContainer”創(chuàng)建會(huì)話并針對(duì)一些數(shù)據(jù)執(zhí)行:
StatelessKieSession kSession = kContainer.newStatelessKieSession(); Applicant applicant = new Applicant( "Mr John Smith", 16 ); assertTrue( applicant.isValid() ); ksession.execute( applicant ); assertFalse( applicant.isValid() );上面的代碼根據(jù)規(guī)則執(zhí)行數(shù)據(jù)。 由于申請(qǐng)人年齡在18歲以下,申請(qǐng)被標(biāo)記為無效。
到目前為止,我們只使用了一個(gè)實(shí)例,但如果我們想要使用多個(gè)實(shí)例呢? 我們可以針對(duì)任何實(shí)現(xiàn)Iterable的對(duì)象執(zhí)行,比如集合。 讓我們添加另一個(gè)名為“Application”的類,它具有應(yīng)用程序的日期,我們也將布爾有效字段移動(dòng)到“Application”類。
public class Applicant {private String name; private int age; // getter and setter methods here } public class Application { private Date dateApplied; private boolean valid; // getter and setter methods here }我們還會(huì)添加另一條規(guī)則來驗(yàn)證申請(qǐng)是在一段時(shí)間內(nèi)完成的。
package com.company.licenserule "Is of valid age" whenApplicant( age < 18 ) $a : Application() then $a.setValid( false ); end rule "Application was made this year" when $a : Application( dateApplied > "01-jan-2009" ) then $a.setValid( false ); end不幸的是,Java數(shù)組并沒有實(shí)現(xiàn)?Iterable?接口,所以我們必須使用JDK轉(zhuǎn)換器方法?Arrays.asList(…?)。 下面顯示的代碼針對(duì)可迭代的列表執(zhí)行,在列表中插入所有集合元素,然后再觸發(fā)任何匹配的規(guī)則。
StatelessKieSession kSession = kContainer.newStatelessKieSession(); Applicant applicant = new Applicant( "Mr John Smith", 16 ); Application application = new Application(); assertTrue( application.isValid() ); ksession.execute( Arrays.asList( new Object[] { application, applicant } ) ); assertFalse( application.isValid() );execute(Object object)?和?execute(Iterable objects )?是接口BatchExecutor方法?execute(Command command)?的兩個(gè)實(shí)現(xiàn)方法。
像KIE API的所有其他工廠一樣,可以從?KieServices?獲得的?KieCommands?命令工廠用于創(chuàng)建命令,以下相當(dāng)于?execute(Iterable it):
``ksession.execute( kieServices.getCommands().newInsertElements( Arrays.asList( new Object[] { application, applicant } ) );``批處理執(zhí)行程序和命令工廠在使用多個(gè)命令和輸出標(biāo)識(shí)符來獲取結(jié)果時(shí)特別有用。
KieCommands kieCommands = kieServices.getCommands(); List<Command> cmds = new ArrayList<Command>(); cmds.add( kieCommands.newInsert( new Person( "Mr John Smith" ), "mrSmith", true, null ) ); cmds.add( kieCommands.newInsert( new Person( "Mr John Doe" ), "mrDoe", true, null ) ); BatchExecutionResults results = ksession.execute( kieCommands.newBatchExecution( cmds ) ); assertEquals( new Person( "Mr John Smith" ), results.getValue( "mrSmith" ) );CommandFactory?支持許多其他可以在?StartProcess?,?Query?和?SetGlobal?的?BatchExecutor?中使用的命令。
有狀態(tài)的知識(shí)Session
有狀態(tài)會(huì)話很長時(shí)間,并允許隨著時(shí)間的推移進(jìn)行迭代更改。有狀態(tài)會(huì)話的一些常見用例包括但不限于:
-
監(jiān)測(cè)
-
半自動(dòng)買入的股市監(jiān)測(cè)和分析
-
-
診斷
-
故障查找,醫(yī)療診斷
-
-
物流
-
包裹跟蹤和交付供應(yīng)
-
-
合規(guī)性
-
驗(yàn)證市場(chǎng)交易的合法性
-
與無狀態(tài)會(huì)話相比,之后必須調(diào)用dispose()方法,以確保沒有內(nèi)存泄漏,因?yàn)镵ieBase在創(chuàng)建時(shí)包含對(duì)有狀態(tài)知識(shí)會(huì)話的引用。由于有狀態(tài)知識(shí)會(huì)話是最常用的會(huì)話類型,因此它在KIE API中被命名為KieSession。KieSession還支持BatchExecutor接口,就像StatelessKieSession一樣,唯一的區(qū)別是FireAllRules命令不會(huì)在有狀態(tài)會(huì)話結(jié)束時(shí)自動(dòng)調(diào)用。
我們以舉一個(gè)火警的例子來說明監(jiān)控用例。我們只用四個(gè)類,代表一間房子里的房間,每間房子里有一個(gè)灑水器。如果一個(gè)房間發(fā)生火災(zāi),我們用一個(gè)“Fire”實(shí)例來表示。
public class Room {private String name // getter and setter methods here } public class Sprinkler { private Room room; private boolean on; // getter and setter methods here } public class Fire { private Room room; // getter and setter methods here } public class Alarm { }在前面關(guān)于無狀態(tài)會(huì)話的章節(jié)中,介紹了插入和匹配數(shù)據(jù)的概念。 這個(gè)例子假設(shè)每個(gè)對(duì)象類型只有一個(gè)實(shí)例被插入,因此只能使用字面約束。 然而,房子有很多房間,所以規(guī)則必須表達(dá)物體之間的關(guān)系,比如噴灑器在某個(gè)房間里。 這最好通過使用綁定變量作為模式中的約束來完成。 這個(gè)“加入”過程產(chǎn)生了所謂的交叉產(chǎn)品,這在下一節(jié)將會(huì)介紹。
發(fā)生火災(zāi)時(shí),會(huì)為該房間創(chuàng)建Fire類的實(shí)例,并將其插入會(huì)話中。 該規(guī)則在Fire對(duì)象的room字段上使用綁定來約束與當(dāng)前關(guān)閉的房間的噴灑器的匹配。 當(dāng)這個(gè)規(guī)則觸發(fā)并且結(jié)果被執(zhí)行時(shí),噴淋器被打開。
rule "When there is a fire turn on the sprinkler" whenFire($room : room)$sprinkler : Sprinkler( room == $room, on == false ) then modify( $sprinkler ) { setOn( true ) }; System.out.println( "Turn on the sprinkler for room " + $room.getName() ); end而無狀態(tài)會(huì)話使用標(biāo)準(zhǔn)的Java語法來修改一個(gè)字段,而在上面的規(guī)則中,我們使用modify語句,它作為一種“with”語句。它可能包含一系列逗號(hào)分隔的Java表達(dá)式,即對(duì)由modify語句的控制表達(dá)式選擇的對(duì)象的設(shè)置者的調(diào)用。這會(huì)修改數(shù)據(jù),并使引擎知道這些更改,以便再次推理它們。這個(gè)過程被稱為推理,對(duì)有狀態(tài)會(huì)話的工作是必不可少的。無狀態(tài)會(huì)話通常不使用推理,因此引擎不需要知道數(shù)據(jù)的更改。也可以通過使用sequential mode顯式關(guān)閉推理。
到目前為止,我們有規(guī)則告訴我們什么時(shí)候匹配數(shù)據(jù)存在,但是什么時(shí)候存在not呢? 我們?nèi)绾未_定火已被撲滅,即不存在一個(gè)Fire對(duì)象了嗎? 以前,約束條件是根據(jù)命題邏輯的句子,其中引擎限制個(gè)別情況。 Drools的也有一階邏輯的支持,可以讓你看的數(shù)據(jù)集。 當(dāng)不存在某個(gè)關(guān)鍵字時(shí),關(guān)鍵字“not”下的模式匹配。 一旦房間里的火已經(jīng)消失,下面給出的規(guī)則就會(huì)使噴灑器關(guān)閉。
rule "When the fire is gone turn off the sprinkler" when$room : Room( )$sprinkler : Sprinkler( room == $room, on == true )not Fire( room == $room ) then modify( $sprinkler ) { setOn( false ) }; System.out.println( "Turn off the sprinkler for room " + $room.getName() ); end每個(gè)房間有一個(gè)灑水噴頭,這個(gè)建筑物只有一個(gè)警報(bào)。 發(fā)生火災(zāi)時(shí)會(huì)產(chǎn)生一個(gè)“Alarm”對(duì)象,但無論發(fā)生多少火災(zāi),整個(gè)建筑物只需要一個(gè)“Alarm”。 以前沒有引入“not”來匹配事實(shí); 現(xiàn)在我們使用它的補(bǔ)充“exists”來匹配某個(gè)類別的一個(gè)或多個(gè)實(shí)例。
rule "Raise the alarm when we have one or more fires" whenexists Fire() theninsert( new Alarm() ); System.out.println( "Raise the alarm" ); end同樣,當(dāng)沒有火災(zāi)時(shí),我們要?jiǎng)h除警報(bào),所以not關(guān)鍵字可以再次使用。
rule "Cancel the alarm when all the fires have gone" whennot Fire()$alarm : Alarm() then delete( $alarm ); System.out.println( "Cancel the alarm" ); end最后,當(dāng)應(yīng)用程序首次啟動(dòng)時(shí),以及在警報(bào)被移除并且所有灑水噴頭已關(guān)閉之后,都會(huì)打印一般健康狀態(tài)消息。
rule "Status output when things are ok" whennot Alarm()not Sprinkler( on == true ) then System.out.println( "Everything is ok" ); end正如我們?cè)跓o狀態(tài)會(huì)話中所做的那樣,上面的規(guī)則應(yīng)該放在一個(gè)DRL文件中,并保存到Maven項(xiàng)目或其任何子文件夾的資源文件夾中。 和以前一樣,我們可以從KieContainer中獲得一個(gè)KieSession。 唯一不同的是,這一次我們創(chuàng)建了一個(gè)有狀態(tài)會(huì)話,而在此之前,我們創(chuàng)建的是一個(gè)無狀態(tài)會(huì)話。
KieServices kieServices = KieServices.Factory.get(); KieContainer kContainer = kieServices.getKieClasspathContainer(); KieSession ksession = kContainer.newKieSession();隨著會(huì)話創(chuàng)建,現(xiàn)在可以反復(fù)使用它隨著時(shí)間的推移。 創(chuàng)建并插入四個(gè)Room對(duì)象,以及每個(gè)房間的一個(gè)sprinkler對(duì)象。 在這一點(diǎn)上,引擎已經(jīng)完成了所有的匹配,但是還沒有任何規(guī)則被解雇。 調(diào)用ksession.fireAllRules()允許匹配的規(guī)則觸發(fā),但沒有火災(zāi),只會(huì)產(chǎn)生健康信息。
String[] names = new String[]{"kitchen", "bedroom", "office", "livingroom"}; Map<String,Room> name2room = new HashMap<String,Room>(); for( String name: names ){ Room room = new Room( name ); name2room.put( name, room ); ksession.insert( room ); Sprinkler sprinkler = new Sprinkler( room ); ksession.insert( sprinkler ); } ksession.fireAllRules(); ``> Everything is ok``我們現(xiàn)在創(chuàng)建兩個(gè)大火并插入它們; 這次為返回的FactHandle保留一個(gè)引用。 事實(shí)句柄是插入實(shí)例的內(nèi)部引擎引用,允許實(shí)例在稍后的時(shí)間點(diǎn)收回或修改。 現(xiàn)在發(fā)動(dòng)機(jī)發(fā)生火災(zāi),一旦調(diào)用了fireAllRules(),報(bào)警就會(huì)被觸發(fā),相應(yīng)的噴淋頭就會(huì)打開。
Fire kitchenFire = new Fire( name2room.get( "kitchen" ) ); Fire officeFire = new Fire( name2room.get( "office" ) );FactHandle kitchenFireHandle = ksession.insert( kitchenFire ); FactHandle officeFireHandle = ksession.insert( officeFire );ksession.fireAllRules(); > Raise the alarm > Turn on the sprinkler for room kitchen > Turn on the sprinkler for room office過了一段時(shí)間后,火勢(shì)將會(huì)消失,Fire實(shí)例將被收回。 這導(dǎo)致灑水器關(guān)閉,警報(bào)被取消,最終再次打印健康信息。
ksession.delete( kitchenFireHandle ); ksession.delete( officeFireHandle );ksession.fireAllRules(); > Cancel the alarm > Turn off the sprinkler for room office > Turn off the sprinkler for room kitchen > Everything is ok這并不難,我希望你能開始看到聲明式規(guī)則系統(tǒng)的價(jià)值和威力。
方法與規(guī)則
人們經(jīng)常混淆方法和規(guī)則,而新規(guī)則用戶經(jīng)常會(huì)問:“我怎樣稱呼規(guī)則?” 在上一節(jié)之后,你現(xiàn)在的感覺就像一個(gè)規(guī)則專家,答案是顯而易見的,但我們?nèi)匀豢偨Y(jié)的差異。
public void helloWorld(Person person) { if ( person.getName().equals( "Chuck" ) ) { System.out.println( "Hello Chuck" ); } }-
方法直接調(diào)用。
-
特定的實(shí)例通過。
-
一次調(diào)用會(huì)導(dǎo)致一次執(zhí)行。
-
規(guī)則通過匹配任何數(shù)據(jù)執(zhí)行,只要它插入引擎。
-
規(guī)則永遠(yuǎn)不能直接調(diào)用。
-
特定的實(shí)例不能傳遞給規(guī)則。
-
根據(jù)比賽情況,規(guī)則可能會(huì)觸發(fā)一次或幾次,或根本不觸發(fā)。
交叉產(chǎn)品
早些時(shí)候提到了“交叉產(chǎn)品”這個(gè)詞,這是加入的結(jié)果。 想象一下,來自火災(zāi)報(bào)警示例的數(shù)據(jù)與沒有字段限制的下列規(guī)則結(jié)合使用:
rule "Show Sprinklers" when$room : Room()$sprinkler : Sprinkler() thenSystem.out.println( "room:" + $room.getName() +" sprinkler:" + $sprinkler.getRoom().getName() ); end用SQL語言來說,就像在Room,?Sprinkler中執(zhí)行select *一樣,Room表中的每一行都將與Sprinkler表中的每一行連接在一起,產(chǎn)生以下輸出:
room:office sprinkler:office room:office sprinkler:kitchen room:office sprinkler:livingroom room:office sprinkler:bedroom room:kitchen sprinkler:office room:kitchen sprinkler:kitchen room:kitchen sprinkler:livingroom room:kitchen sprinkler:bedroom room:livingroom sprinkler:office room:livingroom sprinkler:kitchen room:livingroom sprinkler:livingroom room:livingroom sprinkler:bedroom room:bedroom sprinkler:office room:bedroom sprinkler:kitchen room:bedroom sprinkler:livingroom room:bedroom sprinkler:bedroom這些交叉產(chǎn)品顯然可能變得龐大,并且可能包含虛假數(shù)據(jù)。 交叉產(chǎn)品的大小通常是新規(guī)則制定者性能問題的來源。 由此可以看出,限制交叉產(chǎn)品總是可取的,這是通過可變約束來完成的。
rule when$room : Room()$sprinkler : Sprinkler( room == $room ) thenSystem.out.println( "room:" + $room.getName() +" sprinkler:" + $sprinkler.getRoom().getName() ); end這使得只有四行數(shù)據(jù),每個(gè)房間都有正確的噴淋頭。 在SQL(實(shí)際上是HQL)中,相應(yīng)的查詢將是select * from Room, Sprinkler where Room == Sprinkler.room。
room:office sprinkler:office room:kitchen sprinkler:kitchen room:livingroom sprinkler:livingroom room:bedroom sprinkler:bedroom執(zhí)行控制
議程
議程是一個(gè)Rete功能。 它維護(hù)一組能夠執(zhí)行的規(guī)則,其工作是按照確定的順序安排執(zhí)行。
在RuleRuntime操作期間,規(guī)則可能完全匹配并有資格執(zhí)行; 單個(gè)規(guī)則運(yùn)行時(shí)操作可能會(huì)導(dǎo)致多個(gè)符合條件的規(guī)則。 當(dāng)規(guī)則完全匹配時(shí),創(chuàng)建規(guī)則匹配,引用規(guī)則和匹配的事實(shí),并放置到議程中。 議程使用沖突解決策略控制這些匹配的執(zhí)行順序。
引擎反復(fù)循環(huán)兩個(gè)階段:
1.規(guī)則運(yùn)行時(shí)操作。 這是大部分工作發(fā)生的地方,無論是在結(jié)果(RHS本身)還是主要的Java應(yīng)用程序過程中。 一旦結(jié)果完成或主Java應(yīng)用程序進(jìn)程調(diào)用fireAllRules(),引擎就切換到議程評(píng)估階段。
2.議程評(píng)估。 這試圖選擇一個(gè)規(guī)則來觸發(fā)。 如果沒有找到規(guī)則,則退出,否則會(huì)觸發(fā)找到的規(guī)則,將階段切換回規(guī)則運(yùn)行時(shí)操作。
Figure 1. Two Phase Execution這個(gè)過程重復(fù),直到議程清晰,在這種情況下控制返回到調(diào)用應(yīng)用程序。 當(dāng)規(guī)則運(yùn)行時(shí)操作正在發(fā)生時(shí),沒有規(guī)則被觸發(fā)。
規(guī)則匹配和沖突集
現(xiàn)金流量示例
到目前為止,數(shù)據(jù)和匹配過程是簡單而小巧的。 為了將事情混合起來,我們將探索一個(gè)新的例子來處理日期期間的現(xiàn)金流量計(jì)算。 發(fā)動(dòng)機(jī)的狀態(tài)將在關(guān)鍵階段示例性地示出,以幫助更好地理解發(fā)動(dòng)機(jī)罩下的實(shí)際情況。 將使用三個(gè)類,如下所示。 這將有助于我們?cè)黾訉?duì)模式匹配的理解并進(jìn)一步加入。 然后我們將用這個(gè)來說明執(zhí)行控制的不同技術(shù)。
public class CashFlow {private Date date; private double amount; private int type; long accountNo; // getter and setter methods here } public class Account { private long accountNo; private double balance; // getter and setter methods here } public AccountPeriod { private Date start; private Date end; // getter and setter methods here }現(xiàn)在,您已經(jīng)知道如何創(chuàng)建KieBases以及如何實(shí)例化事實(shí)來填充KieSession,所以將使用表來顯示插入的數(shù)據(jù)的狀態(tài),因?yàn)樗故虑樽兊酶忧宄?下表顯示為“Account”插入單個(gè)事實(shí)。 還插入了一系列借方和貸方作為該賬戶的“CashFlow”對(duì)象,延伸了兩個(gè)季度。
Figure 2. CashFlows and Account兩個(gè)規(guī)則可以用來確定該季度的借方和貸方,并更新賬戶余額。 以下兩條規(guī)則限制了給定時(shí)間段內(nèi)某個(gè)賬戶的現(xiàn)金流量。 請(qǐng)注意使用簡短語法的“&&”,以避免兩次重復(fù)字段名稱。
select * from Account acc, Cashflow cf, AccountPeriod ap where acc.accountNo == cf.accountNo and cf.type == CREDIT and cf.date >= ap.start and cf.date <= ap.end trigger : acc.balance += cf.amount select * from Account acc, Cashflow cf, AccountPeriod ap where acc.accountNo == cf.accountNo and cf.type == DEBIT and cf.date >= ap.start and cf.date <= ap.end trigger : acc.balance -= cf.amount如果AccountPeriod設(shè)置為第一季度,我們將限制增加貸方余額的規(guī)則在兩行數(shù)據(jù)上觸發(fā),減少借方余額以對(duì)一行數(shù)據(jù)進(jìn)行操作。
Figure 3. 會(huì)計(jì)周期,現(xiàn)金流量和賬戶上面的兩個(gè)現(xiàn)金流量表代表兩個(gè)規(guī)則的匹配數(shù)據(jù)。 數(shù)據(jù)在插入階段被匹配,正如你在上一章中發(fā)現(xiàn)的那樣,它不會(huì)直接觸發(fā),而只是在fireAllRules()被調(diào)用之后。 同時(shí),規(guī)則及其匹配的數(shù)據(jù)被放置在議程上,并被稱為“行為匹配”或“規(guī)則實(shí)例”。 議程是一個(gè)規(guī)則匹配表,只要fireAllRules()被調(diào)用,它就能夠觸發(fā)并執(zhí)行其后果。 議程上的規(guī)則匹配被稱為沖突集,其執(zhí)行是由沖突解決策略決定的。 請(qǐng)注意,到目前為止的執(zhí)行順序被認(rèn)為是任意的。
Figure 4. 現(xiàn)金流和賬戶所有上述激活的規(guī)則被執(zhí)行后,賬戶的余額為-25。
Figure 5. 現(xiàn)金流和賬戶如果賬戶期限更新到第二季度,我們只有一行匹配的數(shù)據(jù),因此在議程上只有一個(gè)規(guī)則匹配。
規(guī)則執(zhí)行的結(jié)果導(dǎo)致了25的余額。
Figure 6. 現(xiàn)金流和賬戶 Figure 7. 現(xiàn)金流和賬戶解決沖突
如果你不希望規(guī)則執(zhí)行的順序是任意的呢? 如果議程上有一個(gè)或多個(gè)規(guī)則匹配,則說明它們之間存在沖突,并使用沖突解決策略來確定執(zhí)行順序。 Drools戰(zhàn)略非常簡單,基于一個(gè)顯著的價(jià)值,它為一個(gè)規(guī)則賦予了優(yōu)先權(quán)。 每個(gè)規(guī)則的默認(rèn)值為0,值越高,優(yōu)先級(jí)越高。
作為一般規(guī)則,不要指望以任何特定順序開火的規(guī)則,并且制定規(guī)則而不必?fù)?dān)心“flow”。 然而,當(dāng)需要流程時(shí),除了顯著性之外還有許多可能性:議程組,規(guī)則流程組,激活組和控制/信號(hào)量事實(shí)。
Drools 6.0 規(guī)則根據(jù)源文件中的salience之后的數(shù)字來確定優(yōu)先級(jí)。
Salience
為了說明salience,我們添加一個(gè)規(guī)則來打印帳戶余額,我們希望在對(duì)所有帳戶應(yīng)用所有借記和貸項(xiàng)之后執(zhí)行此規(guī)則。 我們通過給這個(gè)規(guī)則分配一個(gè)負(fù)的salience來達(dá)到這個(gè)目的,以便在默認(rèn)salience 0的所有規(guī)則之后觸發(fā)。
rule "Print balance for AccountPeriod"salience -50whenap : AccountPeriod()acc : Account()thenSystem.out.println( acc.accountNo + " : " + acc.balance ); end下表描述了由此產(chǎn)生的議程。 這三個(gè)借記和貸記規(guī)則顯示為任意順序,而打印規(guī)則排在最后,以后執(zhí)行。
Figure 8. 現(xiàn)金流和賬戶議程組agenda group
議程組允許您將規(guī)則放入組中,并將這些組放入堆棧。 該堆棧有push/pop 的能力。 調(diào)用“setFocus”將組放入堆棧:
ksession.getAgenda().getAgendaGroup( "Group A" ).setFocus();議程總是評(píng)估堆棧的頂部。 當(dāng)所有的規(guī)則已經(jīng)為一個(gè)組激發(fā),它從堆棧彈出,并評(píng)估下一個(gè)組。
rule "increase balance for credits"agenda-group "calculation" whenap : AccountPeriod()acc : Account( $accountNo : accountNo )CashFlow( type == CREDIT,accountNo == $accountNo,date >= ap.start && <= ap.end,$amount : amount ) thenacc.balance += $amount; end rule "Print balance for AccountPeriod"agenda-group "report" whenap : AccountPeriod()acc : Account() thenSystem.out.println( acc.accountNo +" : " + acc.balance ); end首先把重點(diǎn)放在“report”組,然后把重點(diǎn)放在“calculation”上,我們確保首先評(píng)估組。
Agenda agenda = ksession.getAgenda(); agenda.getAgendaGroup( "report" ).setFocus(); agenda.getAgendaGroup( "calculation" ).setFocus(); ksession.fireAllRules();規(guī)則流程
Drools還具有ruleflow-group屬性,允許工作流程圖聲明性地指定何時(shí)允許激發(fā)規(guī)則。 下面的截圖是從Eclipse使用Drools插件。 它有兩個(gè)規(guī)則流組節(jié)點(diǎn),確保計(jì)算規(guī)則在報(bào)告規(guī)則之前執(zhí)行。
在規(guī)則中使用ruleflow-group屬性如下所示。
rule "increase balance for credits"ruleflow-group "calculation" whenap : AccountPeriod()acc : Account( $accountNo : accountNo )CashFlow( type == CREDIT,accountNo == $accountNo,date >= ap.start && <= ap.end,$amount : amount ) thenacc.balance += $amount; end rule "Print balance for AccountPeriod"ruleflow-group "report" whenap : AccountPeriod()acc : Account() thenSystem.out.println( acc.accountNo +" : " + acc.balance ); end推理
巴士通行證例子
現(xiàn)在推論有一個(gè)不好的名字,因?yàn)樗c業(yè)務(wù)用例無關(guān),而且太復(fù)雜而無用。 的確,人為的和復(fù)雜的例子都是在推論中出現(xiàn)的,但是這也不應(yīng)該損害簡單有用的存在。 但更重要的是,正確使用推理可以提供更敏捷,更不容易出錯(cuò)的業(yè)務(wù)規(guī)則,這些規(guī)則更容易維護(hù)。
那么推理是什么? 當(dāng)我們通過使用以前的知識(shí)獲得某些東西的知識(shí)時(shí),就會(huì)推斷出什么 例如,給定一個(gè)具有年齡字段和規(guī)定年齡政策控制的規(guī)則的人的事實(shí),我們可以推斷出一個(gè)人是成年人還是孩子,并據(jù)此采取行動(dòng)。
rule "Infer Adult" when$p : Person( age >= 18 ) theninsert( new IsAdult( $p ) ) end由于前面的規(guī)則,每個(gè)18歲以上的人都會(huì)為他們插入一個(gè)IsAdult的實(shí)例。 這個(gè)事實(shí)是特殊的,因?yàn)樗环Q為關(guān)系。 我們可以在任何規(guī)則中使用這個(gè)推斷關(guān)系:
$p : Person() IsAdult( person == $p )所以現(xiàn)在我們知道推論是什么,并且有一個(gè)基本的例子,這是如何促進(jìn)良好的規(guī)則設(shè)計(jì)和維護(hù)?
讓孩子成年后負(fù)責(zé)發(fā)放身份證的政府部門,以下簡稱ID部門。 他們可能有一個(gè)決策表,其中包括這樣的邏輯,它說當(dāng)一個(gè)在倫敦的成年人是18歲或以上,發(fā)卡:
但身份證件部門并沒有制定成人的政策。這是在中央政府一級(jí)完成的。如果中央政府將這個(gè)年齡改為21歲,這將啟動(dòng)變革管理過程。有人必須聯(lián)系身份證件部門,確保他們的系統(tǒng)得到更新,以便法律上線。
這種變更管理流程和部門之間的溝通對(duì)于敏捷環(huán)境來說并不理想,而且變更成本高昂且容易出錯(cuò)。此外,信用卡部門正在管理更多的信息,而不是需要通過其“規(guī)模管理”的“單一”方法意識(shí)到這一點(diǎn)。我的意思是,它并不關(guān)心明確的年齡>= 18信息決定某人是否是成年人,而只關(guān)心他們是否是成年人。
相比之下,讓我們采取一種方法,將創(chuàng)作責(zé)任分開(脫離),使中央政府和身份證部門都保持自己的規(guī)則。
確定誰是成年人是中央政府的工作。 如果他們改變法律,他們只是用新的規(guī)則來更新他們的中央倉庫,
如前所述,IsAdult事實(shí)是從政策規(guī)則中推斷出來的。 它封裝了看似隨意的一段邏輯時(shí)間?= 18,并為其含義提供了語義抽象。 現(xiàn)在,如果有人使用上述規(guī)則,他們不再需要知道明確的信息,決定某人是否是成年人。 他們可以使用推斷的事實(shí):
雖然這個(gè)例子是非常微小的,但它說明了一些重要的觀點(diǎn)。我們從知識(shí)工程的單一和漏洞入手開始。我們創(chuàng)建了一個(gè)包含所有可能的信息的決策表,泄露了ID部門不關(guān)心也不想管理的來自中央政府的信息。
我們首先將知識(shí)過程分離開來,這樣每個(gè)部門只負(fù)責(zé)知道什么。然后,我們使用推斷的事實(shí)IsAdult封裝了這個(gè)變動(dòng)的知識(shí)。術(shù)語IsAdult的使用還給以前的任意邏輯時(shí)間?= 18提供了語義抽象。
所以在進(jìn)行知識(shí)工程時(shí),一般的經(jīng)驗(yàn)法則是:
-
壞的
-
單一Monolithic
-
泄露Leaky
-
好的
-
解除對(duì)知識(shí)的責(zé)任
-
封裝知識(shí)
-
為這些封裝提供語義抽象
-
用邏輯對(duì)象維護(hù)真相
概述
定期插入后,你必須明確地收回事實(shí)。有了邏輯斷言,斷言的事實(shí)將會(huì)自動(dòng)撤回,因?yàn)閿嘌运臈l件不再是真實(shí)的。實(shí)際上,它更加聰明,因?yàn)橹挥挟?dāng)沒有任何單一條件支持邏輯斷言時(shí)才會(huì)收回。
聲明一個(gè)正規(guī)的插入,就像“陳述事實(shí)”所暗示的直覺意義一樣。使用一個(gè)HashMap和一個(gè)計(jì)數(shù)器,我們追蹤一個(gè)特定的平等是多少次;這意味著我們計(jì)算有多少不同的實(shí)例是相等的。
當(dāng)我們?cè)赗HS執(zhí)行期間邏輯插入一個(gè)對(duì)象的時(shí)候,我們被說成是正當(dāng)?shù)?#xff0c;并且被認(rèn)為是通過觸發(fā)規(guī)則來證明的。對(duì)于每個(gè)邏輯插入,只能有一個(gè)相等的對(duì)象,并且每個(gè)隨后的相等的邏輯插入增加該邏輯斷言的對(duì)齊計(jì)數(shù)器。創(chuàng)建規(guī)則的LHS取消了正當(dāng)理由,計(jì)數(shù)器也相應(yīng)減少。一旦我們沒有更多的理由,邏輯對(duì)象自動(dòng)收回。
如果我們嘗試邏輯插入一個(gè)對(duì)象時(shí),有一個(gè)相等的聲明對(duì)象,這將失敗,并返回null。如果我們聲明一個(gè)對(duì)象具有一個(gè)現(xiàn)有的平等的對(duì)象,我們重寫事實(shí);這個(gè)覆蓋如何工作取決于配置設(shè)置WM_BEHAVIOR_PRESERVE。當(dāng)屬性設(shè)置為discard時(shí),我們使用現(xiàn)有的句柄,并用新的對(duì)象替換現(xiàn)有的實(shí)例,這是默認(rèn)行為;否則我們重寫它,同時(shí)我們創(chuàng)建一個(gè)新的FactHandle。
這可能會(huì)在第一次閱讀時(shí)感到困惑,所以希望下面的流程圖有所幫助。當(dāng)它說它返回一個(gè)新的FactHandle時(shí),這也表示對(duì)象是通過網(wǎng)絡(luò)傳播的。
Figure 9. 聲明插入 Figure 10. 邏輯插入有推理和TMS的公共汽車通行證例子
前面的例子是發(fā)行身份證超過18歲,在這個(gè)例子中,我們現(xiàn)在發(fā)行巴士通行證,無論是兒童或成人通行證。
rule "Issue Child Bus Pass" when$p : Person( age < 16 ) theninsert(new ChildBusPass( $p ) ); end rule "Issue Adult Bus Pass" when $p : Person( age >= 16 ) then insert(new AdultBusPass( $p ) ); end像以前一樣,上面的例子被認(rèn)為是單一的,漏洞和提供差的關(guān)注分離。
像以前一樣,我們可以提供一個(gè)更強(qiáng)大的應(yīng)用程序,使用推理來區(qū)分問題。注意這次我們不只是插入推斷的對(duì)象,我們使用“insertLogical”:
rule "Infer Child" when$p : Person( age < 16 ) theninsertLogical( new IsChild( $p ) ) end rule "Infer Adult" when $p : Person( age >= 16 ) then insertLogical( new IsAdult( $p ) ) end“insertLogical”是Drools真相維護(hù)系統(tǒng)(TMS)的一部分。當(dāng)一個(gè)事實(shí)被邏輯插入時(shí),這個(gè)事實(shí)取決于“when”從句的真實(shí)性。這意味著,當(dāng)規(guī)則成為錯(cuò)誤時(shí),事實(shí)會(huì)自動(dòng)收回。由于這兩個(gè)規(guī)則是相互排斥的,所以這個(gè)效果特別好。所以在上面的規(guī)則中,如果這個(gè)人在16歲以下,它會(huì)插入一個(gè)IsChild的事實(shí),一旦這個(gè)人是16歲或以上,IsChild的事實(shí)就會(huì)自動(dòng)收回,并且插入了IsAdult的事實(shí)。
回到代碼發(fā)出巴士通行證,這兩個(gè)規(guī)則可以在邏輯上插入ChildBusPass和AdultBusPass事實(shí),因?yàn)門MS +支持鏈接一系列級(jí)聯(lián)的邏輯插入。
rule "Issue Child Bus Pass" when$p : Person( )IsChild( person == $p ) theninsertLogical(new ChildBusPass( $p ) ); endrule "Issue Adult Bus Pass" when $p : Person( age >= 16 ) IsAdult( person =$p ) then insertLogical(new AdultBusPass( $p ) ); end現(xiàn)在當(dāng)一個(gè)人從15歲變成16歲時(shí),不僅IsChild事實(shí)自動(dòng)縮回,他的ChildBusPass事實(shí)也是如此。對(duì)于獎(jiǎng)勵(lì)積分,我們可以將這個(gè)與'not'條件元素結(jié)合起來處理通知,在這種情況下,請(qǐng)求返回通行證。所以當(dāng)TMS自動(dòng)收回ChildBusPass對(duì)象時(shí),這個(gè)規(guī)則觸發(fā)并發(fā)送一個(gè)請(qǐng)求給這個(gè)人:
rule "Return ChildBusPass Request "when$p : Person( )not( ChildBusPass( person == $p ) ) thenrequestChildBusPass( $p ); end重要說明:Java對(duì)象的等價(jià)
注意到真理維護(hù)(和邏輯斷言)完全可以工作,這是很重要的,你的Fact對(duì)象(可能是JavaBeans)必須正確地覆蓋equals和hashCode方法(來自java.lang.Object)。由于事實(shí)維護(hù)系統(tǒng)需要知道兩個(gè)不同物理對(duì)象的值是否相等,因此按照J(rèn)ava標(biāo)準(zhǔn),必須正確覆蓋both等于和hashCode。
兩個(gè)對(duì)象是相等的,當(dāng)且僅當(dāng)它們的equals方法相互返回true,并且它們的hashCode方法返回相同的值。有關(guān)更多詳細(xì)信息,請(qǐng)參閱Java API(但請(qǐng)記住,MUST必須覆蓋equals和hashCode)。
TMS行為不受標(biāo)識(shí)vs等價(jià)的時(shí)間配置的影響,TMS始終是等價(jià)的。
從工作記憶中刪除陳述或邏輯斷言
默認(rèn)情況下,當(dāng)從工作記憶中刪除一個(gè)事實(shí)時(shí),Drools嘗試從既定事實(shí)集合中刪除它,并且在邏輯斷言的情況下也從真值維護(hù)系統(tǒng)TMS中刪除它。但是,使用delete方法的重載,也可以只從2.中刪除它。例如,調(diào)用:
``ksession.delete( factHandle, FactHandle.State.LOGICAL );``這個(gè)事實(shí)只有在邏輯斷言的情況下才會(huì)被刪除,但是如果它是一個(gè)陳述的事實(shí)則不會(huì)被刪除。在這種情況下,如果事實(shí)已經(jīng)說明了它的刪除失敗了,并且被忽略了。
電子表格中的決策表
決策表是一種“精確而緊湊”(參考自Wikipedia)表示條件邏輯的方式,非常適合商業(yè)級(jí)規(guī)則。
Drools支持電子表格格式的管理規(guī)則。支持的格式是Excel(XLS)和CSV,這意味著可以使用各種電子表格程序(例如Microsoft Excel,OpenOffice.org Calc等)。預(yù)計(jì)基于網(wǎng)絡(luò)的決策表編輯器將被包括在不久的將來版本中。
決策表是一個(gè)舊的概念(用軟件來說),但是多年來已經(jīng)證明是有用的。簡而言之,在Drools中,決策表是一種生成從輸入到電子表格中的數(shù)據(jù)驅(qū)動(dòng)的規(guī)則的方法。數(shù)據(jù)采集和處理的電子表格的所有常用功能都可以利用。
何時(shí)使用決策表
如果存在可以表示為規(guī)則模板和數(shù)據(jù)的規(guī)則,則將決策表視為一個(gè)行為過程:決策表的每一行都提供與模板結(jié)合生成規(guī)則的數(shù)據(jù)。
許多企業(yè)已經(jīng)使用電子表格來管理數(shù)據(jù),計(jì)算等。如果您樂于繼續(xù)這種方式,您也可以通過這種方式來管理您的業(yè)務(wù)規(guī)則。這也假設(shè)您很樂于在xls或csv文件中管理規(guī)則包。決策表不建議用于不遵循一組模板的規(guī)則,也不建議使用少量規(guī)則(或者對(duì)Excel或OpenOffice.org等軟件不喜歡)。它們是理想的,可以控制規(guī)則的參數(shù)可以編輯,而不需要直接暴露規(guī)則。
決策表還提供了一定程度的基礎(chǔ)對(duì)象模型的隔離。
概述
下面是一些真實(shí)世界決策表的例子(稍作修改以保護(hù)無辜者)。
Figure 11. 使用Excel來編輯決策表 Figure 12. 一個(gè)規(guī)則行中多種行為 Figure 13. 使用OpenOffice.org在上面的例子中,決策表的技術(shù)方面已經(jīng)崩潰了(使用標(biāo)準(zhǔn)的電子表格功能)。
規(guī)則從第17行開始,每一行產(chǎn)生一個(gè)規(guī)則。條件在列C,D,E等中,動(dòng)作在屏幕外。單元格中的值非常簡單,它們的含義由行16中的標(biāo)題指示。列B只是一個(gè)描述。通常使用顏色來明確表格的不同區(qū)域的含義。
| ? | 請(qǐng)注意,雖然決策表看起來像自上而下,但情況并非如此。理想情況下,規(guī)則的編寫不考慮行的順序,僅僅因?yàn)檫@使得維護(hù)更容易,因?yàn)樾胁恍枰恢币苿?dòng)。 |
由于每一行都是一個(gè)規(guī)則,所以適用相同的原則。由于規(guī)則引擎處理事實(shí),任何匹配的規(guī)則都可能觸發(fā)。 (有些人對(duì)此感到困惑,在規(guī)則觸發(fā)時(shí)模擬一個(gè)非常簡單的決策表就可以清除議程,只有第一個(gè)匹配才會(huì)執(zhí)行一個(gè)動(dòng)作。)另請(qǐng)注意,在一個(gè)電子表格中可以有多個(gè)表。這樣,可以將規(guī)則分組在共享通用模板的位置,然而在一天結(jié)束時(shí),它們?nèi)拷M合成一個(gè)規(guī)則包。決策表本質(zhì)上是一種自動(dòng)生成DRL規(guī)則的工具。
Figure 14. 使用多個(gè)表進(jìn)行分組的規(guī)則決策表如何工作
要記住的關(guān)鍵是,決策表中的每一行都是一個(gè)規(guī)則,該行中的每一列都是該規(guī)則的條件或動(dòng)作。
Figure 15. 行和列電子表格查找RuleTable關(guān)鍵字以指示規(guī)則表(起始行和列)的開始。 其他關(guān)鍵字也用于定義其他包級(jí)別屬性(稍后介紹)。 將關(guān)鍵字保留在一列是很重要的。 按照慣例,第二列(“B”)用于這個(gè),但它可以是任何列(約定左邊留有空白的備注)。 在下面的圖中,C實(shí)際上是它開始的列。 左邊的所有內(nèi)容都被忽略。
如果我們擴(kuò)大隱藏的部分,它開始變得更有意義它是如何工作的; 請(qǐng)注意C列中的關(guān)鍵字。
Figure 16. 展開規(guī)則模板現(xiàn)在可以看到使其工作的隱藏的魔法。 RuleSet關(guān)鍵字指示將在rule package中使用的名稱,該名稱將包含所有規(guī)則。 此名稱是可選的,使用默認(rèn)值,但必須在單元格右邊有RuleSet關(guān)鍵字。
列C中可見的其他關(guān)鍵字是“導(dǎo)入”和“順序”,稍后將對(duì)其進(jìn)行介紹。 RuleTable關(guān)鍵字是很重要的,因?yàn)樗硎緦⒆裱恍┮?guī)則,基于一些規(guī)則模板。 RuleTable關(guān)鍵字后面有一個(gè)名稱,用于為生成的規(guī)則的名稱添加前綴。 附加表單名稱和行號(hào)以保證唯一的規(guī)則名稱。
| ? | 與表格名稱結(jié)合使用的表格名稱在同一KieBase中的所有電子表格文件中必須是唯一的。 如果不是這樣,一些規(guī)則可能會(huì)有相同的名稱,只有其中一個(gè)會(huì)被應(yīng)用。 要顯示這樣被忽略的規(guī)則,https://docs.jboss.org/drools/release/7.4.1.Final/drools-docs/html_single/index.html#_changingthedefaultbuildresultresultseverity [引發(fā)此類規(guī)則名稱沖突的嚴(yán)重性]。 |
RuleTable的列表示規(guī)則開始的列; 左側(cè)的列將被忽略。
| ? | 通常,關(guān)鍵字組成名稱 - 值對(duì)。 |
參考第14行(RuleTable之后的那一行),關(guān)鍵字CONDITION和ACTION表示下面各列中的數(shù)據(jù)適用于規(guī)則的LHS部分或RHS部分。 規(guī)則上還有其他屬性,也可以選擇這種方式設(shè)置。
第15行包含ObjectTypes的聲明。 該行中的內(nèi)容是可選的,但是如果該選項(xiàng)未被使用,則該行必須留空; 然而這個(gè)選項(xiàng)通常被認(rèn)為是相當(dāng)有用的。 使用此行時(shí),下面單元格(第16行)中的值將成為該對(duì)象類型的約束。 在上面的例子中,它生成了“Person(age ==”42“)”和“Cheese(type ==”stilton“)”,其中42和“stilton”來自第18行。 ,“==”是隱含的; 如果只給出一個(gè)字段名稱,翻譯者就會(huì)假定它是生成完全匹配的。
| ? | 一個(gè)ObjectType聲明可以跨越列(通過合并的單元格),這意味著合并范圍以下的所有列將被合并到一個(gè)模式中,一次匹配一個(gè)事實(shí)的一個(gè)模式中,而非包含 相同的ObjectType,但導(dǎo)致不同的模式,可能匹配不同或相同的事實(shí)。 |
第16行包含規(guī)則模板本身。 他們可以使用“$ param”占位符來表示下面單元格的數(shù)據(jù)應(yīng)該插入的位置。 (對(duì)于多次插入,請(qǐng)使用“$ 1”,“$ 2”等,表示下面單元格中逗號(hào)分隔列表中的參數(shù)。 它可能包含該欄目的文字說明。
行18和行19顯示的數(shù)據(jù)將與行15中的模板組合(插值),以生成規(guī)則。 如果一個(gè)單元格不包含數(shù)據(jù),則其模板將被忽略。 (這意味著某些條件或操作不適用于該規(guī)則行。)讀取規(guī)則行直到出現(xiàn)空行。 一張表中可以存在多個(gè)RuleTables。 第20行包含另一個(gè)關(guān)鍵字和一個(gè)值。 像這樣的關(guān)鍵字的行位置并不重要(大多數(shù)人把它們放在頂部),但是它們的列應(yīng)該與RuleTable或RuleSet關(guān)鍵字出現(xiàn)的位置相同。 在我們的情況下,列C被選擇為重要的,但是可以使用任何其他列。
在上面的例子中,規(guī)則會(huì)像下面一樣呈現(xiàn)(因?yàn)樗褂谩癘bjectType”行):
//row 18 rule "Cheese_fans_18" whenPerson(age=="42") Cheese(type=="stilton") then list.add("Old man stilton"); end| ? | “age ==”42“”和“type ==”stilton“”的約束被解釋為單個(gè)約束,被添加到上面單元格的相應(yīng)ObjectType中。 如果上面的單元格是跨越的,那么在一個(gè)“列”上可能有多個(gè)約束。 |
| ? | 非常大的決策表可能有非常大的內(nèi)存要求。 |
電子表格語法
電子表格結(jié)構(gòu)
有兩種類型的矩形區(qū)域定義用于生成DRL文件的數(shù)據(jù)。 一個(gè)由標(biāo)記為“RuleSet”的單元標(biāo)記,定義除規(guī)則以外的所有DRL項(xiàng)目。 另一個(gè)可能會(huì)重復(fù)發(fā)生,并且位于以“RuleTable”開頭的單元格的右下方。 這些領(lǐng)域代表了實(shí)際的決策表,每個(gè)領(lǐng)域產(chǎn)生了一套類似結(jié)構(gòu)的規(guī)則。
一個(gè)規(guī)則集區(qū)域可以包含單元對(duì),一個(gè)在RuleSet下面,包含一個(gè)關(guān)鍵字,指定在同一行中的另一個(gè)關(guān)鍵字中包含的值。
規(guī)則表區(qū)域的列定義了從中派生的規(guī)則左側(cè)的模式和約束,規(guī)則后果的動(dòng)作以及單個(gè)規(guī)則屬性的值。因此,規(guī)則表區(qū)域應(yīng)該包含一個(gè)或多個(gè)列,條件和操作,以及規(guī)則屬性的列的任意選擇,每個(gè)列最多一列。前面四行跟著標(biāo)記有“RuleTable”單元格的行被標(biāo)記為標(biāo)題區(qū)域,主要用于定義構(gòu)建規(guī)則的代碼。它是這四個(gè)標(biāo)題行下面的任何額外的行,產(chǎn)生另一個(gè)規(guī)則,其數(shù)據(jù)提供規(guī)則表標(biāo)題中定義的代碼的變化。
所有關(guān)鍵字不區(qū)分大小寫。
只有第一張工作表被檢查決策表。
規(guī)則集條目
規(guī)則集區(qū)域中的條目可以定義DRL構(gòu)造(規(guī)則除外),并指定規(guī)則屬性。 盡管可以重復(fù)使用結(jié)構(gòu)的條目,但是每個(gè)規(guī)則屬性最多只能給出一次,并且它適用于所有規(guī)則,除非它被“規(guī)則表”區(qū)域內(nèi)定義的相同屬性取代。
條目必須以垂直堆疊的單元對(duì)序列給出。 第一個(gè)包含關(guān)鍵字和右邊的值,如下表所示。 只要由“RuleSet”標(biāo)記的列被維護(hù)為包含關(guān)鍵字的那一列,這個(gè)單元格對(duì)的序列就可以被空行或甚至規(guī)則表中斷。
| 關(guān)鍵字 | 值 | 用法 |
| RuleSet | 生成的DRL文件的包名稱。可選,默認(rèn)是lele_table。 | 必須是第一個(gè)入口。 |
| Sequential | "true" or "false". 如果是“true”,則使用顯著性來確保規(guī)則從上到下起火。 | 可選,最多一次。如果省略,則不執(zhí)行射擊命令。 |
| SequentialMaxPriority | 整數(shù)數(shù)值 | 可選,最多一次。在順序模式下,此選項(xiàng)用于設(shè)置突出顯示的起始值。如果省略,則默認(rèn)值為65535。 |
| SequentialMinPriority | 整數(shù)數(shù)值 | 可選,最多一次。在順序模式下,此選項(xiàng)用于檢查是否違反了最小顯著性值。如果省略,則默認(rèn)值為0。 |
| EscapeQuotes | "true" or "false". 如果是“true”,那么引號(hào)就會(huì)被轉(zhuǎn)義,從而在DRL中出現(xiàn)。 | 可選,最多一次。如果省略,引號(hào)將被轉(zhuǎn)義。 |
| NumericDisabled | "true" or "false". 如果是“true”,則字符串表示形式用于DRL,而不是來自Numeric單元格的double值可選,最多一次。如果省略,則使用double值。 | Import |
| 要導(dǎo)入的Java類的逗號(hào)分隔列表。 | 可選,可以重復(fù)使用。 | 變量 |
| DRL全局變量的聲明,即一個(gè)后跟一個(gè)變量名的類型。多個(gè)全局定義必須用逗號(hào)分隔。 | 可選,可以重復(fù)使用。 | Variables |
| DRL全局變量的聲明,即一個(gè)后跟一個(gè)變量名的類型。多個(gè)全局定義必須用逗號(hào)分隔。 | 可選,可以重復(fù)使用。 | Functions |
| 一個(gè)或多個(gè)函數(shù)定義,根據(jù)DRL語法。 | 可選,可以重復(fù)使用。 | Queries |
| 一個(gè)或多個(gè)查詢定義,根據(jù)DRL語法。 | 可選,可以重復(fù)使用。 | Declare |
| 在某些語言環(huán)境中,MS Office,LibreOffice和OpenOffice會(huì)對(duì)不同的雙引號(hào)進(jìn)行編碼,這會(huì)導(dǎo)致編譯錯(cuò)誤,通常很難看出差異,例如:“A”會(huì)失敗, 但"A"會(huì)起作用。 |
| ? | 要定義適用于生成的DRL文件中所有規(guī)則的規(guī)則屬性,可以使用下表中的任何條目。 但是請(qǐng)注意,必須使用正確的關(guān)鍵字。 而且,每個(gè)屬性只能使用一次。 |
| Keyword | Initial | Value |
| PRIORITY | P | 一個(gè)定義規(guī)則“顯著性”值的整數(shù)。由“順序”標(biāo)志覆蓋。 |
| DURATION | D | 定義規(guī)則的“持續(xù)時(shí)間”值的長整數(shù)值。 |
| TIMER | T | 定時(shí)器定義。請(qǐng)參閱“定時(shí)器和日歷”。 |
| ENABLED | B | 一個(gè)布爾值。 “真”使規(guī)則成為可能; “false”會(huì)禁用規(guī)則。 |
| CALENDARS | E | 日歷定義。請(qǐng)參閱“定時(shí)器和日歷”。 |
| NO-LOOP | U | 一個(gè)布爾值。 “真”禁止由于其結(jié)果所做的更改而循環(huán)的規(guī)則。 |
| LOCK-ON-ACTIVE | L | 一個(gè)布爾值。 “true”禁止在同一個(gè)規(guī)則流或議程組中設(shè)置此標(biāo)志的所有規(guī)則的額外激活。 |
| AUTO-FOCUS | F | 一個(gè)布爾值。對(duì)于議程組中的規(guī)則而言,“真實(shí)”會(huì)導(dǎo)致規(guī)則的激活,從而自動(dòng)將焦點(diǎn)集中到組中。 |
| ACTIVATION-GROUP | X | 識(shí)別激活(或XOR)組的字符串。激活組中只有一個(gè)規(guī)則將被觸發(fā),即第一個(gè)激活組將取消同一組內(nèi)其他規(guī)則的任何激活。 |
| AGENDA-GROUP | G | 一個(gè)標(biāo)識(shí)一個(gè)議程組的字符串,必須通過賦予其“焦點(diǎn)”來激活,這是控制規(guī)則組之間流動(dòng)的一種方式。 |
| RULEFLOW-GROUP | R | 標(biāo)識(shí)規(guī)則流組的字符串。 |
規(guī)則表
所有規(guī)則表都以一個(gè)包含“RuleTable”的單元格開始,可選地在同一個(gè)單元格中跟隨一個(gè)字符串。該字符串用作從該規(guī)則表派生的所有規(guī)則的名稱的起始部分,附加行號(hào)以區(qū)分。 (這個(gè)自動(dòng)命名可以通過使用NAME列來覆蓋。)定義此Rule Table規(guī)則的所有其他單元格位于該單元格的下方和右側(cè)。
下一行定義了列的類型,每列產(chǎn)生一部分條件或結(jié)果,或者提供一些規(guī)則屬性,規(guī)則名稱或注釋。下表顯示了哪些列標(biāo)題可用;根據(jù)顯示前一節(jié)中給出的規(guī)則屬性條目的表格,可以使用額外的列。請(qǐng)注意,每個(gè)屬性列最多只能使用一次。對(duì)于列標(biāo)題,請(qǐng)使用關(guān)鍵字或任何其他以這些表格的“初始”列中給出的字母開頭的單詞。
| Keyword | Initial | Value | Usage |
| NAME | N | 提供從該行生成的規(guī)則的名稱。 默認(rèn)值是根據(jù)RuleTable標(biāo)簽和行號(hào)之后的文本構(gòu)造的。 | 最多只有一列 |
| DESCRIPTION | I | 一個(gè)文本,在生成的規(guī)則中產(chǎn)生一個(gè)注釋。 | 最多只有一列 |
| CONDITION | C | 代碼片段和插值,用于在條件中的模式中構(gòu)建約束。 | 每個(gè)規(guī)則表至少有一個(gè) |
| ACTION | A | 代碼片斷和插值用于構(gòu)建規(guī)則后果的操作。 | 每個(gè)規(guī)則表至少有一個(gè) |
| METADATA | @ | 代碼片斷和插值用于構(gòu)建規(guī)則的元數(shù)據(jù)條目。 | 可選,任意數(shù)量的列 |
給定一個(gè)標(biāo)題為CONDITION的列,連續(xù)行中的單元格產(chǎn)生一個(gè)條件元素。
-
CONDITION下面的第一個(gè)單元格中的文本發(fā)展成規(guī)則條件的模式,下一行中的代碼段成為約束條件。如果單元格與一個(gè)或多個(gè)鄰居合并,則形成具有多個(gè)約束的單個(gè)模式:將所有約束合并到一個(gè)帶括號(hào)的列表中,并附加到該單元格中的文本。單元格可能會(huì)留空,這意味著下一行中的代碼片段必須單獨(dú)生成有效的條件元素。 要包含沒有約束的模式,可以將模式寫在另一個(gè)模式的文本前面。 模式可以寫有或沒有一個(gè)空的括號(hào)。 “from”子句可以附加到模式。 如果模式以“eval”結(jié)尾,代碼片段應(yīng)該產(chǎn)生布爾表達(dá)式,以包含在“eval”之后的一對(duì)括號(hào)中。
-
CONDITION下面的第二個(gè)單元格中的文本分兩步處理。
-
這個(gè)單元格中的代碼片段是通過插入列中更靠下的單元格中的值來修改的。如果要使用“==”創(chuàng)建一個(gè)由下面的單元格的值組成的約束條件,則僅字段選擇器就足夠了。任何其他比較運(yùn)算符都必須被指定為代碼片段中的最后一項(xiàng),并且下面單元格的值被附加。對(duì)于所有其他約束形式,您必須標(biāo)記位置以包含符號(hào)“$ param”的單元格的內(nèi)容。通過在下面的單元格中使用符號(hào)“$ 1”,“$ 2”等和逗號(hào)分隔值列表,可以實(shí)現(xiàn)多重插入。 根據(jù)模式forall(delimiter){snippet}的文本的文本通過對(duì)每個(gè)單元格中的逗號(hào)分隔值列表的每個(gè)值重復(fù)snippet來展開在下面插入值代替符號(hào)$并且通過給定的delimiter加入這些擴(kuò)展。請(qǐng)注意,該構(gòu)造可能被其他文本包圍。
-
如果前一行中的單元格不是空的,則將完成的代碼片段添加到該單元格的條件元素中。一對(duì)括號(hào)是自動(dòng)提供的,如果將多個(gè)約束添加到合并單元格中的模式,則會(huì)自動(dòng)提供分隔逗號(hào)。 如果上面的單元格是空的,插入的結(jié)果將按原樣使用。
-
CONDITION下方的第三個(gè)單元格中的文本僅用于文檔。它應(yīng)該用來向讀者指出專欄的目的。
-
從第四行開始,非空白條目提供如上所述的插值數(shù)據(jù)。空白單元格導(dǎo)致省略該規(guī)則的條件元素或約束條件。 給出一個(gè)以行動(dòng)為首的專欄,連續(xù)行中的單元格產(chǎn)生一個(gè)行動(dòng)陳述。
-
ACTION下面的第一個(gè)單元格中的文本是可選的。如果存在,則將其解釋為對(duì)象引用。
-
ACTION下面第二個(gè)單元格中的文本分兩步處理。
-
這個(gè)單元格中的代碼片段是通過插入列中更靠下的單元格中的值來修改的。對(duì)于單數(shù)插入,用符號(hào)“$ param”標(biāo)記包含單元格內(nèi)容的位置。通過在下面的單元格中使用符號(hào)“$ 1”,“$ 2”等和逗號(hào)分隔值列表,可以實(shí)現(xiàn)多重插入。 沒有內(nèi)插的方法調(diào)用可以通過沒有任何標(biāo)記符號(hào)的文本來實(shí)現(xiàn)。在這種情況下,請(qǐng)使用下面一行中的任何非空白條目來包含該語句。 這個(gè)構(gòu)造也可以在這里找到。 2.如果第一個(gè)單元格不是空的,其文本,后面是一個(gè)句點(diǎn),第二個(gè)單元格中的文本和終止分號(hào)被串聯(lián)在一起,產(chǎn)生一個(gè)方法調(diào)用,作為結(jié)果的一個(gè)動(dòng)作語句添加。 如果上面的單元格是空的,插入的結(jié)果將按原樣使用。
-
ACTION下面的第三個(gè)單元格中的文本僅用于文檔。它應(yīng)該用來向讀者指出專欄的目的。
-
從第四行開始,非空白條目提供如上所述的插值數(shù)據(jù)。空白單元格導(dǎo)致省略此規(guī)則的操作語句。
| ? | 在大多數(shù)情況下,使用“$ 1”而不是“$ param”工作,但如果替換文本包含逗號(hào),則會(huì)失敗:然后,只插入第一個(gè)逗號(hào)前面的部分。 明智地使用這個(gè)“縮寫”。 |
給定一個(gè)以METADATA開頭的列,連續(xù)行中的單元格將為生成的規(guī)則生成元數(shù)據(jù)注釋。
-
METADATA下面第一個(gè)單元格中的文本被忽略。
-
如上所述,METADATA下面的第二個(gè)單元格中的文本使用來自規(guī)則行單元格的值進(jìn)行插值。元數(shù)據(jù)標(biāo)記字符“@”是自動(dòng)添加的,因此它不應(yīng)該包含在這個(gè)單元格的文本中。
-
METADATA下面的第三個(gè)單元格中的文本僅用于文檔。它應(yīng)該用來向讀者指出專欄的目的。
-
從第四行開始,非空白條目提供如上所述的插值數(shù)據(jù)。空白單元格導(dǎo)致省略此規(guī)則的元數(shù)據(jù)注釋。
例子
以下示例說明了各種插值。 例85.插值單元格數(shù)據(jù) 如果模板是“Foo(bar == $ param)”,單元格是“42”,那么結(jié)果是“Foo(bar == 42)”。
如果模板是“Foo(bar <$ 1,baz == $ 2)”,而單元格包含“42,43”,則結(jié)果將是“Foo(bar <42,baz == 43) 。
模板forall(&&){bar!= $}與一個(gè)包含42,43的單元格產(chǎn)生bar!= 42 && bar!= 43。
下一個(gè)例子演示了定義模式類型的單元和下面的代碼片段的共同作用。
<br class="Apple-interchange-newline">296/5000該電子表格部分顯示了Person類型聲明如何跨越2列,因此這兩個(gè)約束將以Person(age == …?,type == …?)的形式出現(xiàn)。 由于只有字段名稱存在于片段中,因此意味著平等測(cè)試。
在下面的例子中使用了標(biāo)志符號(hào)$ param。
這個(gè)列的結(jié)果是模式Person(age ==“42”))。 您可能已經(jīng)注意到標(biāo)記和運(yùn)算符“==”是多余的。
下一個(gè)例子說明了一個(gè)尾隨的插入標(biāo)記可以省略。
在這里,從單元格中附加值是隱含的,導(dǎo)致Person(年齡<42))。
您可以提供綁定變量的定義,如下例所示。
這里的結(jié)果是c:Cheese(type ==“stilton”)。 請(qǐng)注意,報(bào)價(jià)是自動(dòng)提供的。 實(shí)際上,任何東西都可以放在對(duì)象類型的行中。 除了綁定變量的定義之外,它也可以是一個(gè)附加的模式,可以直接插入。
下面顯示了插入單個(gè)值的操作語句的簡單構(gòu)造。
ACTION標(biāo)題下面的單元格留空。 使用這種風(fēng)格,任何事情都可以放在結(jié)果中,而不僅僅是一個(gè)方法調(diào)用。 (同樣的技術(shù)也適用于CONDITION列。)
下面是一個(gè)綜合的例子,展示了各種列標(biāo)題的使用。 在列標(biāo)題之下沒有任何值是錯(cuò)誤的(如在NO-LOOP列中):在這里,屬性不會(huì)被應(yīng)用在任何規(guī)則中。
Figure 17. 關(guān)鍵字用于導(dǎo)入,標(biāo)題等的示例用法最后,這里是一個(gè)導(dǎo)入,變量和函數(shù)的例子。
Figure 18. 功能關(guān)鍵字的使用示例等同一單元格中的多個(gè)軟件包名稱必須用逗號(hào)分隔。此外,類型和變量名稱對(duì)必須用逗號(hào)分隔。但是,函數(shù)必須按照出現(xiàn)在DRL文件中的方式編寫。這應(yīng)該與“RuleSet”關(guān)鍵字出現(xiàn)在同一列;它可以在所有規(guī)則行之上,之間或之下。
| ? | It may be more convenient to use Import, Variables, Functions and Queries repeatedly rather than packing several definitions into a single cell. |
創(chuàng)建和集成基于電子表格的決策表
使用基于電子表格的決策表的API位于drools-decisiontables模塊中。實(shí)際上只有一個(gè)類可以看:SpreadsheetCompiler。這個(gè)類將采取各種格式的電子表格,并在DRL中生成規(guī)則(然后可以以正常的方式使用)。 SpreadsheetCompiler可以用來生成部分規(guī)則文件(如果需要的話),然后在事實(shí)之后將其組裝成一個(gè)完整的規(guī)則包(如果需要,可以將規(guī)則的技術(shù)和非技術(shù)方面分開)。
要開始,可以使用示例電子表格作為基礎(chǔ)。或者,如果正在使用插件(Rule Workbench IDE),則向?qū)Э梢詮哪0迳呻娮颖砀?#xff08;編輯它需要使用xls兼容的電子表格編輯器)。
Figure 19. IDE中的向?qū)?管理決策表中的業(yè)務(wù)規(guī)則
工作流程和協(xié)作
電子表格是完善的商業(yè)工具(使用了25年以上)。決策表適合密切IT和領(lǐng)域?qū)<抑g的合作,同時(shí)使業(yè)務(wù)分析人員明確業(yè)務(wù)規(guī)則,這是一個(gè)理想的分離關(guān)注點(diǎn)。
通常,編寫規(guī)則的整個(gè)過程(提出一個(gè)新的決策表)將是這樣的:
業(yè)務(wù)分析師采用模板決策表(從存儲(chǔ)庫或從IT)
決策表業(yè)務(wù)語言描述被輸入到表格中
決策表規(guī)則(行)被輸入(大致)
決策表交給技術(shù)資源,他將業(yè)務(wù)語言(描述)映射到腳本(當(dāng)然,這可能涉及軟件開發(fā),如果是新的應(yīng)用程序或數(shù)據(jù)模型)
技術(shù)人員交回業(yè)務(wù)分析師的修改。
業(yè)務(wù)分析師可以根據(jù)需要繼續(xù)編輯規(guī)則行(移動(dòng)列周圍也很好等)。
同時(shí),技術(shù)人員可以為規(guī)則開發(fā)測(cè)試用例(與業(yè)務(wù)分析人員聯(lián)系),因?yàn)橐坏┫到y(tǒng)運(yùn)行,這些測(cè)試用例可用于驗(yàn)證規(guī)則和規(guī)則更改。
使用電子表格功能
Excel等應(yīng)用程序的功能可用于幫助將數(shù)據(jù)輸入電子表格,例如驗(yàn)證字段。存儲(chǔ)在其他工作表中的列表可用于為單元格提供有效的值列表,如下圖所示。
一些應(yīng)用程序提供了有限的能力來保存更改的歷史記錄,但是建議使用另一種版本控制方法。當(dāng)規(guī)則隨時(shí)間發(fā)生變化時(shí),舊版本將被歸檔(許多開源解決方案,例如Subversion或Git)。
規(guī)則模板
與決策表(但不一定需要電子表格)相關(guān)的是“規(guī)則模板”(在drools-templates模塊中)。這些使用任何表格數(shù)據(jù)源作為規(guī)則數(shù)據(jù)的來源 - 填充模板以生成許多規(guī)則。這既可以用于更靈活的電子表格,也可以用于現(xiàn)有數(shù)據(jù)庫中的規(guī)則(以開發(fā)模板為代價(jià)來生成規(guī)則)。
使用規(guī)則模板,數(shù)據(jù)與規(guī)則是分開的,并且規(guī)則的哪一部分是數(shù)據(jù)驅(qū)動(dòng)的沒有限制。所以盡管你可以在決策表中做所有事情,但你也可以做以下的事情:
-
將數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(或其他格式)
-
有條件地根據(jù)數(shù)據(jù)中的值生成規(guī)則
-
為規(guī)則的任何部分使用數(shù)據(jù)(例如條件運(yùn)算符,類名稱,屬性名稱)
-
通過相同的數(shù)據(jù)運(yùn)行不同的模板。例如,顯示了一個(gè)更經(jīng)典的決策表,但沒有規(guī)則元數(shù)據(jù)的任何隱藏行(所以電子表格只包含生成規(guī)則的原始數(shù)據(jù))。
請(qǐng)參閱上述電子表格示例下載中的ExampleCheese.xls。
如果這是一個(gè)常規(guī)決策表,那么在行1之前以及包含規(guī)則元數(shù)據(jù)的行1和行2之間將存在隱藏行。使用規(guī)則模板,數(shù)據(jù)與規(guī)則完全分開。這有兩個(gè)方便的后果 - 您可以將多個(gè)規(guī)則模板應(yīng)用于相同的數(shù)據(jù),并且您的數(shù)據(jù)根本不依賴于您的規(guī)則。那么模板是什么樣的?
1 template header 2 age 3 type 4 log 5 6 package org.drools.examples.templates; 7 8 global java.util.List list; 9 10 template "cheesefans" 11 12 rule "Cheese fans_@{row.rowNumber}" 13 when 14 Person(age == @{age}) 15 Cheese(type == "@{type}") 16 then 17 list.add("@{log}"); 18 end 19 20 end template上述程序清單的注釋:
-
第1行:所有規(guī)則模板以template header開始。
-
第2-4行:在標(biāo)題之后是按照它們?cè)跀?shù)據(jù)中出現(xiàn)的順序排列的列表。在這種情況下,我們稱第一列時(shí)間,第二種類型和第三個(gè)日志。
-
第5行:空行表示列定義的結(jié)束。
-
第6-9行:標(biāo)準(zhǔn)規(guī)則標(biāo)題文本。這是DRL的標(biāo)準(zhǔn)規(guī)則,將出現(xiàn)在生成的DRL的頂部。將package語句和任何導(dǎo)入以及全局和函數(shù)定義放入本節(jié)。
-
第10行:關(guān)鍵字模板表示規(guī)則模板的開始。模板文件中可以有多個(gè)模板,但每個(gè)模板都應(yīng)該有唯一的名稱。
-
第11-18行:規(guī)則模板 - 詳見下文。
-
第20行:關(guān)鍵字結(jié)束模板表示模板的結(jié)尾。
規(guī)則模板依靠MVEL使用語法@ {token_name}進(jìn)行替換。目前有一個(gè)內(nèi)置的表達(dá)式:@ {row.rowNumber},它為每一行數(shù)據(jù)提供唯一的編號(hào),并使您能夠生成唯一的規(guī)則名稱。對(duì)于每一行數(shù)據(jù),都會(huì)生成一個(gè)規(guī)則,用數(shù)據(jù)中的值代替模板中的令牌。
規(guī)則模板必須包含在擴(kuò)展名為.drt的文件中,并且在定義kmodule.xml文件中的kbase時(shí)與相應(yīng)的決策表相關(guān)聯(lián),如下例所示
<?xml version="1.0" encoding="UTF-8"?> <kmodule xmlns="http://drools.org/xsd/kmodule"><kbase name="TemplatesKB" packages="org.drools.examples.templates"> <ruleTemplate dtable="org/drools/examples/templates/ExampleCheese.xls" template="org/drools/examples/templates/Cheese.drt" row="2" col="2"/> <ksession name="TemplatesKS"/> </kbase> </kmodule>通過上面的示例數(shù)據(jù),將生成以下規(guī)則文件:
package org.drools.examples.templates;global java.util.List list;rule "Cheese fans_1" whenPerson(age == 42) Cheese(type == "stilton") then list.add("Old man stilton"); end rule "Cheese fans_2" when Person(age == 21) Cheese(type == "cheddar") then list.add("Young man cheddar"); end此時(shí),名為“TemplatesKS”的KieSession包含從模板生成的規(guī)則,可以簡單地從KieContainer創(chuàng)建,并用作其他KieSession。
KieSession ksession = kc.newKieSession( "TemplatesKS" );// now create some test data ksession.insert( new Cheese( "stilton", 42 ) ); ksession.insert( new Person( "michael", "stilton", 42 ) ); final List<String> list = new ArrayList<String>(); ksession.setGlobal( "list", list ); ksession.fireAllRules();記錄日志
照亮作為規(guī)則引擎的黑盒子的一種方法是玩日志級(jí)別。
一切都記錄到SLF4J,這是一個(gè)簡單的日志記錄門面,可以將任何日志委托給Logback,Apache Commons Logging,Log4j或java.util.logging。將日志適配器的依賴關(guān)系添加到您選擇的日志框架中。如果你還沒有使用任何日志框架,你可以通過添加這個(gè)Maven依賴項(xiàng)來使用Logback:
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.x</version></dependency>| ? | 如果您正在開發(fā)超輕型環(huán)境,請(qǐng)使用slf4j-nop或slf4j-simple。 |
在包org.drools上配置日志記錄級(jí)別。例如:
在Logback中,將其配置到您的logback.xml文件中:
<configuration><logger name="org.drools" level="debug"/>...<configuration>在Log4J中,將其配置到您的log4j.xml文件中:
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"><category name="org.drools"><priority value="debug" /></category>...</log4j:configuration>轉(zhuǎn)載于:https://www.cnblogs.com/prpl/p/7875328.html
總結(jié)
以上是生活随笔為你收集整理的Drools 7.4.1.Final参考手册(六) 用户手册的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VMware15下载及使用教程
- 下一篇: 更精炼更专注的RTMPClient客户端