<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    進(jìn)行中  
    。。。
    日歷
    <2006年8月>
    303112345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789
    統(tǒng)計(jì)
    • 隨筆 - 13
    • 文章 - 1
    • 評(píng)論 - 3
    • 引用 - 0

    導(dǎo)航

    常用鏈接

    留言簿(1)

    隨筆檔案

    文章檔案

    相冊(cè)

    link

    搜索

    •  

    最新隨筆

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

     

    使用 Drools 規(guī)則引擎實(shí)現(xiàn)業(yè)務(wù)邏輯

    使用聲明性編程方法編寫程序的業(yè)務(wù)邏輯

    developerWorks
    文檔選項(xiàng)
    將此頁(yè)作為電子郵件發(fā)送

    將此頁(yè)作為電子郵件發(fā)送

    未顯示需要 JavaScript 的文檔選項(xiàng)

    樣例代碼


    最新推薦

    Java 應(yīng)用開(kāi)發(fā)源動(dòng)力 - 下載免費(fèi)軟件,快速啟動(dòng)開(kāi)發(fā)


    級(jí)別: 中級(jí)

    Ricardo Olivieri (roliv@us.ibm.com), 軟件工程師, IBM

    2006 年 6 月 19 日

    使用規(guī)則引擎可以通過(guò)降低實(shí)現(xiàn)復(fù)雜業(yè)務(wù)邏輯的組件的復(fù)雜性,降低應(yīng)用程序的維護(hù)和可擴(kuò)展性成本。本文展示了如何使用 Drools 規(guī)則引擎讓 Java? 應(yīng)用程序更適應(yīng)變化。Drools 的一個(gè)好處是具有允許將 Java 代碼直接嵌入規(guī)則文件中的附加語(yǔ)法。

    要求施加在當(dāng)今軟件產(chǎn)品上的大多數(shù)復(fù)雜性是行 為和功能方面的,從而導(dǎo)致組件實(shí)現(xiàn)具有復(fù)雜的業(yè)務(wù)邏輯。實(shí)現(xiàn) J2EE 或 J2SE 應(yīng)用程序中業(yè)務(wù)邏輯最常見(jiàn)的方法是編寫 Java 代碼來(lái)實(shí)現(xiàn)需求文檔的規(guī)則和邏輯。在大多數(shù)情況下,該代碼的錯(cuò)綜復(fù)雜性使得維護(hù)和更新應(yīng)用程序的業(yè)務(wù)邏輯成為一項(xiàng)令人畏懼的任務(wù),甚至對(duì)于經(jīng)驗(yàn)豐富的開(kāi)發(fā) 人員來(lái)說(shuō)也是如此。任何更改,不管多么簡(jiǎn)單,仍然會(huì)產(chǎn)生重編譯和重部署成本。

    規(guī)則引擎試圖解決(或者至少降低)應(yīng)用程序業(yè)務(wù)邏輯的開(kāi)發(fā)和維護(hù)中固有的問(wèn)題和困難。可以將規(guī)則引擎看作實(shí)現(xiàn)復(fù)雜業(yè)務(wù)邏輯的框架。大多數(shù)規(guī)則引擎允 許您使用聲明性編程來(lái)表達(dá)對(duì)于某些給定信息或知識(shí)有效的結(jié)果。您可以專注于已知為真的事實(shí)及其結(jié)果,也就是應(yīng)用程序的業(yè)務(wù)邏輯。

    有多個(gè)規(guī)則引擎可供使用,其中包括商業(yè)和開(kāi)放源碼選擇。商業(yè)規(guī)則引擎通常允許使用專用的類似英語(yǔ)的語(yǔ)言來(lái)表達(dá)規(guī)則。其他規(guī)則引擎允許使用腳本語(yǔ)言 (比如 Groovy 或 Python)編寫規(guī)則。本文為您介紹 Drools 引擎,并使用示例程序幫助您理解如何使用 Drools 作為 Java 應(yīng)用程序中業(yè)務(wù)邏輯層的一部分。

    更多事情在變化……

    俗話說(shuō)得好,“惟一不變的是變化。”軟件應(yīng)用程序的業(yè)務(wù)邏輯正是如此,尤其是當(dāng)今開(kāi)發(fā)的軟件應(yīng)用程序。出于以下原因,實(shí)現(xiàn)應(yīng)用程序業(yè)務(wù)邏輯的組件可能必須更改:

    • 在開(kāi)發(fā)期間或部署后修復(fù)代碼缺陷
    • 應(yīng)付特殊狀況,即客戶一開(kāi)始沒(méi)有提到要將業(yè)務(wù)邏輯考慮在內(nèi)
    • 處理客戶已更改的業(yè)務(wù)目標(biāo)
    • 符合組織對(duì)敏捷或迭代開(kāi)發(fā)過(guò)程的使用

    如果存在這些可能性,則迫切需要一個(gè)無(wú)需太多復(fù)雜性就能處理業(yè)務(wù)邏輯更改的應(yīng)用程序,尤其是當(dāng)更改復(fù)雜 if-else 邏輯的開(kāi)發(fā)人員并不是以前編寫代碼的開(kāi)發(fā)人員時(shí)。

    Drools 是用 Java 語(yǔ)言編寫的開(kāi)放源碼規(guī)則引擎,使用 Rete 算法(參閱 參考資料) 對(duì)所編寫的規(guī)則求值。Drools 允許使用聲明方式表達(dá)業(yè)務(wù)邏輯??梢允褂?Java/XML 語(yǔ)法編寫規(guī)則,這對(duì)于入門 Drools 十分有用,因?yàn)槟梢詫?Java 代碼直接嵌入規(guī)則文件中。還可以使用 Groovy/XML 語(yǔ)法或 Python/XML 語(yǔ)法在 Drools 中編寫規(guī)則。Drools 還具有其他優(yōu)點(diǎn):

    • 非常活躍的社區(qū)
    • 易用
    • 快速的執(zhí)行速度
    • 在 Java 開(kāi)發(fā)人員中流行
    • JSR 94 兼容(JSR 94 是 Java Rule Engine API)(參閱 參考資料
    • 免費(fèi)

    因此,您應(yīng)該熟悉使用 Eclipse IDE 開(kāi)發(fā) Java 代碼。您應(yīng)該熟悉 JUnit 測(cè)試框架并知道如何在 Eclipse 中使用。您還應(yīng)該相當(dāng)了解 XML。

    要解決的問(wèn)題

    本文展示如何使用 Drools 作為示例 Java 應(yīng)用程序中業(yè)務(wù)邏輯層的一部分。下列假設(shè)為應(yīng)用程序解決的虛構(gòu)問(wèn)題設(shè)置了場(chǎng)景:

    • 名為 XYZ 的公司構(gòu)建兩種類型的計(jì)算機(jī)機(jī)器:Type1 和 Type2。機(jī)器類型按其架構(gòu)定義。

    • XYZ 計(jì)算機(jī)可以提供多種功能。當(dāng)前定義了四種功能:DDNS Server、DNS Server、Gateway 和 Router。

    • 在發(fā)運(yùn)每臺(tái)機(jī)器之前,XYZ 在其上執(zhí)行多個(gè)測(cè)試。

    • 在每臺(tái)機(jī)器上執(zhí)行的測(cè)試取決于每臺(tái)機(jī)器的類型和功能。目前,定義了五種測(cè)試:Test1、Test2、Test3、Test4 和 Test5。

    • 當(dāng)將測(cè)試分配給計(jì)算機(jī)時(shí),也將測(cè)試到期日期 分配給機(jī)器。分配給計(jì)算機(jī)的測(cè)試不能晚于該到期日期執(zhí)行。到期日期值取決于分配給機(jī)器的測(cè)試。

    • XYZ 使用可以確定機(jī)器類型和功能的內(nèi)部開(kāi)發(fā)的軟件應(yīng)用程序,自動(dòng)化了執(zhí)行測(cè)試時(shí)的大部分過(guò)程。然后,基于這些屬性,應(yīng)用程序確定要執(zhí)行的測(cè)試及其到期日期。

    • 目前,為計(jì)算機(jī)分配測(cè)試和測(cè)試到期日期的邏輯是該應(yīng)用程序的已編譯代碼的一部分。包含該邏輯的組件用 Java 語(yǔ)言編寫。

    • 分配測(cè)試和到期日期的邏輯一個(gè)月更改多次。當(dāng)開(kāi)發(fā)人員需要使用 Java 代碼實(shí)現(xiàn)該邏輯時(shí),必須經(jīng)歷一個(gè)冗長(zhǎng)乏味的過(guò)程。
    何時(shí)使用規(guī)則引擎?

    并非所有應(yīng)用程序都應(yīng)使用規(guī)則引擎。如果業(yè)務(wù)邏輯代碼包括很多 if-else 語(yǔ)句,則應(yīng)考慮使用其中一個(gè)。維護(hù)復(fù)雜的 Boolean 邏輯可能是非常困難的任務(wù),而規(guī)則引擎可以幫助您組織該邏輯。當(dāng)您可以使用聲明方法而非命令編程語(yǔ)言表達(dá)邏輯時(shí),變化引入錯(cuò)誤的可能性會(huì)大大降低。

    如果代碼變化可能導(dǎo)致大量的財(cái)政損失,則也應(yīng)考慮規(guī)則引擎。許多組織在將已編譯代碼部署到托管環(huán)境中時(shí)具有嚴(yán)格的規(guī)則。例如,如果需要修改 Java 類中的邏輯,在更改進(jìn)入生產(chǎn)環(huán)境之前,將會(huì)經(jīng)歷一個(gè)冗長(zhǎng)乏味的過(guò)程:

    1. 必須重新編譯應(yīng)用程序代碼。
    2. 在測(cè)試中轉(zhuǎn)環(huán)境中刪除代碼。
    3. 由數(shù)據(jù)質(zhì)量審核員檢查代碼。
    4. 由托管環(huán)境架構(gòu)師批準(zhǔn)更改。
    5. 計(jì)劃代碼部署。

    即使對(duì)一行代碼的簡(jiǎn)單更改也可能花費(fèi)組織的幾千美元。如果需要遵循這些嚴(yán)格規(guī)則并且發(fā)現(xiàn)您頻繁更改業(yè)務(wù)邏輯代碼,則非常有必要考慮使用規(guī)則引擎。

    對(duì)客戶的了解也是該決策的一個(gè)因素。盡管您使用的是一個(gè)簡(jiǎn)單的需求集合,只需 Java 代碼中的簡(jiǎn)單實(shí)現(xiàn),但是您可能從上一個(gè)項(xiàng)目得知,您的客戶具有在開(kāi)發(fā)周期期間甚至部署之后添加和更改業(yè)務(wù)邏輯需求的傾向(以及財(cái)政和政治資源)。如果從一 開(kāi)始就選擇使用規(guī)則引擎,您可能會(huì)過(guò)得舒服一些。

    因?yàn)樵趯?duì)為計(jì)算機(jī)分配測(cè)試和到期日期的邏輯進(jìn)行更改時(shí),公司會(huì)發(fā)生高額成本,所以 XYZ 主管已經(jīng)要求軟件工程師尋找一種靈活的方法,用最少的代價(jià)將對(duì)業(yè)務(wù)規(guī)則的更改 “推” 至生產(chǎn)環(huán)境。于是 Drools 走上舞臺(tái)了。工程師決定,如果它們使用規(guī)則引擎來(lái)表達(dá)確定哪些測(cè)試應(yīng)該執(zhí)行的規(guī)則,則可以節(jié)省更多時(shí)間和精力。他們將只需要更改規(guī)則文件的內(nèi)容,然后在生 產(chǎn)環(huán)境中替換該文件。對(duì)于他們來(lái)說(shuō),這比更改已編譯代碼并在將已編譯代碼部署到生產(chǎn)環(huán)境中時(shí)進(jìn)行由組織強(qiáng)制的冗長(zhǎng)過(guò)程要簡(jiǎn)單省時(shí)得多(參閱側(cè)欄 何時(shí)使用規(guī)則引擎?)。

    目前,在為機(jī)器分配測(cè)試和到期日期時(shí)必須遵循以下業(yè)務(wù)規(guī)則:

    • 如果計(jì)算機(jī)是 Type1,則只能在其上執(zhí)行 Test1、Test2 和 Test5。

    • 如果計(jì)算機(jī)是 Type2 且其中一個(gè)功能為 DNS Server,則應(yīng)執(zhí)行 Test4 和 Test5。

    • 如果計(jì)算機(jī)是 Type2 且其中一個(gè)功能為 DDNS Server,則應(yīng)執(zhí)行 Test2 和 Test3。

    • 如果計(jì)算機(jī)是 Type2 且其中一個(gè)功能為 Gateway,則應(yīng)執(zhí)行 Test3 和 Test4。

    • 如果計(jì)算機(jī)是 Type2 且其中一個(gè)功能為 Router,則應(yīng)執(zhí)行 Test1 和 Test3。

    • 如果 Test1 是要在計(jì)算機(jī)上執(zhí)行的測(cè)試之一,則測(cè)試到期日期距離機(jī)器的創(chuàng)建日期 3 天。該規(guī)則優(yōu)先于測(cè)試到期日期的所有下列規(guī)則。

    • 如果 Test2 是要在計(jì)算機(jī)上執(zhí)行的測(cè)試之一,則測(cè)試到期日期距離機(jī)器的創(chuàng)建日期 7 天。該規(guī)則優(yōu)先于測(cè)試到期日期的所有下列規(guī)則。

    • 如果 Test3 是要在計(jì)算機(jī)上執(zhí)行的測(cè)試之一,則測(cè)試到期日期距離機(jī)器的創(chuàng)建日期 10 天。該規(guī)則優(yōu)先于測(cè)試到期日期的所有下列規(guī)則。

    • 如果 Test4 是要在計(jì)算機(jī)上執(zhí)行的測(cè)試之一,則測(cè)試到期日期距離機(jī)器的創(chuàng)建日期 12 天。該規(guī)則優(yōu)先于測(cè)試到期日期的所有下列規(guī)則。

    • 如果 Test5 是要在計(jì)算機(jī)上執(zhí)行的測(cè)試之一,則測(cè)試到期日期距離機(jī)器的創(chuàng)建日期 14 天。

    捕獲為機(jī)器分配測(cè)試和測(cè)試到期日期的上述業(yè)務(wù)規(guī)則的當(dāng)前 Java 代碼如清單 1 所示:


    清單 1. 使用 if-else 語(yǔ)句實(shí)現(xiàn)業(yè)務(wù)規(guī)則邏輯
    Machine machine = ...
    // Assign tests
    Collections.sort(machine.getFunctions());
    int index;

    if (machine.getType().equals("Type1")) {
    Test test1 = ...
    Test test2 = ...
    Test test5 = ...
    machine.getTests().add(test1);
    machine.getTests().add(test2);
    machine.getTests().add(test5);
    } else if (machine.getType().equals("Type2")) {
    index = Collections.binarySearch(machine.getFunctions(), "Router");
    if (index >= 0) {
    Test test1 = ...
    Test test3 = ...
    machine.getTests().add(test1);
    machine.getTests().add(test3);
    }
    index = Collections.binarySearch(machine.getFunctions(), "Gateway");
    if (index >= 0) {
    Test test4 = ...
    Test test3 = ...
    machine.getTests().add(test4);
    machine.getTests().add(test3);
    }
    ...
    }

    // Assign tests due date
    Collections.sort(machine.getTests(), new TestComparator());
    ...
    Test test1 = ...
    index = Collections.binarySearch(machine.getTests(), test1);
    if (index >= 0) {
    // Set due date to 3 days after Machine was created
    Timestamp creationTs = machine.getCreationTs();
    machine.setTestsDueTime(...);
    return;
    }

    index = Collections.binarySearch(machine.getTests(), test2);
    if (index >= 0) {
    // Set due date to 7 days after Machine was created
    Timestamp creationTs = machine.getCreationTs();
    machine.setTestsDueTime(...);
    return;
    }
    ...

    清單 1 中的代碼不是太復(fù)雜,但也并不簡(jiǎn)單。如果要對(duì)其進(jìn)行更改,需要十分小心。一堆互相纏繞的 if-else 語(yǔ)句正試圖捕獲已經(jīng)為應(yīng)用程序標(biāo)識(shí)的業(yè)務(wù)邏輯。如果您對(duì)業(yè)務(wù)規(guī)則不甚了解,就無(wú)法一眼看出代碼的意圖。





    回頁(yè)首


    導(dǎo)入示例程序

    使用 Drools 規(guī)則的示例程序附帶在本文的 ZIP 存檔中(參閱 下載)。程序使用 Drools 規(guī)則文件以聲明方法表示上一節(jié)定義的業(yè)務(wù)規(guī)則。我建議您在繼續(xù)之前下載 ZIP 存檔。它包含要導(dǎo)入到 Eclipse 工作區(qū)的 Eclipse (v3.1) Java 項(xiàng)目。選擇該選項(xiàng)以導(dǎo)入 Existing Projects into Workspace(參見(jiàn)圖 1):


    圖 1. 將示例程序?qū)氲?Eclipse 工作區(qū)
    將示例程序?qū)氲?Eclipse 工作區(qū)

    然后選擇下載的存檔文件并將其導(dǎo)入工作區(qū)中。您將在工作區(qū)中發(fā)現(xiàn)一個(gè)名為 DroolsDemo 的新 Java 項(xiàng)目,如圖 2 所示:


    圖 2. 導(dǎo)入到工作區(qū)中的示例程序
    導(dǎo)入到 Eclipse 工作區(qū)中的示例程序

    如果啟用了 Build automatically 選項(xiàng),則代碼應(yīng)該已編譯并可供使用。如果未啟用該選項(xiàng),則現(xiàn)在構(gòu)建 DroolsDemo 項(xiàng)目。





    回頁(yè)首


    檢查代碼

    現(xiàn)在來(lái)看一下示例程序中的代碼。該程序的 Java 類的核心集合位于 demo 包中。在該包中可以找到 MachineTest 域?qū)ο箢悺?code>Machine 類的實(shí)例表示要分配測(cè)試和測(cè)試到期日期的計(jì)算機(jī)機(jī)器。下面來(lái)看 Machine 類,如清單 2 所示:


    清單 2. Machine 類的實(shí)例變量
    public class Machine {

    private String type;
    private List functions = new ArrayList();
    private String serialNumber;
    private Collection tests = new HashSet();
    private Timestamp creationTs;
    private Timestamp testsDueTime;

    public Machine() {
    super();
    this.creationTs = new Timestamp(System.currentTimeMillis());
    }
    ...

    在清單 2 中可以看到 Machine 類的屬性有:

    • type(表示為 string 屬性)—— 保存機(jī)器的類型值。
    • functions(表示為 list)—— 保存機(jī)器的功能。
    • testsDueTime(表示為 timestamp 變量)—— 保存分配的測(cè)試到期日期值。
    • testsCollection 對(duì)象)—— 保存分配的測(cè)試集合。

    注意,可以為機(jī)器分配多個(gè)測(cè)試,而且一個(gè)機(jī)器可以具有一個(gè)或多個(gè)功能。

    出于簡(jiǎn)潔目的,機(jī)器的創(chuàng)建日期值設(shè)置為創(chuàng)建 Machine 類的實(shí)例時(shí)的當(dāng)前時(shí)間。如果這是真實(shí)的應(yīng)用程序,創(chuàng)建時(shí)間將設(shè)置為機(jī)器最終構(gòu)建完成并準(zhǔn)備測(cè)試的實(shí)際時(shí)間。

    Test 類的實(shí)例表示可以分配給機(jī)器的測(cè)試。Test實(shí)例由其 idname 惟一描述,如清單 3 所示:


    清單 3. Test 類的實(shí)例變量
    																				
    public class Test {

    public static Integer TEST1 = new Integer(1);
    public static Integer TEST2 = new Integer(2);
    public static Integer TEST3 = new Integer(3);
    public static Integer TEST4 = new Integer(4);
    public static Integer TEST5 = new Integer(5);

    private Integer id;
    private String name;
    private String description;
    public Test() {
    super();
    }
    ...

    示例程序使用 Drools 規(guī)則引擎對(duì) Machine 類的實(shí)例求值。基于 Machine 實(shí)例的 typefunctions 屬性的值,規(guī)則引擎確定應(yīng)分配給 teststestsDueTime 屬性的值。

    demo 包中,還會(huì)發(fā)現(xiàn) Test 對(duì)象的數(shù)據(jù)訪問(wèn)對(duì)象 (TestDAOImpl) 的實(shí)現(xiàn),它允許您按照 ID 查找 Test 實(shí)例。該數(shù)據(jù)訪問(wèn)對(duì)象極其簡(jiǎn)單;它不連接任何外部資源(比如關(guān)系數(shù)據(jù)庫(kù))以獲得 Test 實(shí)例。相反,在其定義中硬編碼了預(yù)定義的 Test 實(shí)例集合。在現(xiàn)實(shí)世界中,您可能會(huì)具有連接外部資源以檢索 Test 對(duì)象的數(shù)據(jù)訪問(wèn)對(duì)象。

    RulesEngine 類

    demo 中比較重要(如果不是最重要的)的一個(gè)類是 RulesEngine 類。該類的實(shí)例用作封裝邏輯以訪問(wèn) Drools 類的包裝器對(duì)象。可以在您自己的 Java 項(xiàng)目中容易地重用該類,因?yàn)樗倪壿嫴皇翘囟ㄓ谑纠绦虻摹G鍐?4 展示了該類的屬性和構(gòu)造函數(shù):


    清單 4. RulesEngine 類的實(shí)例變量和構(gòu)造函數(shù)
    																				
    public class RulesEngine {

    private static Logger logger = Logger.getLogger(RulesEngine.class);

    private RuleBase rules;
    private String rulesFile;
    private boolean debug = false;

    public RulesEngine(String rulesFile) throws RulesEngineException {
    super();
    this.rulesFile = rulesFile;
    try {
    rules = RuleBaseLoader.loadFromInputStream(this.getClass()
    .getResourceAsStream("/rules/" + rulesFile));
    } catch (Exception e) {
    throw new RulesEngineException("Could not load rules file: "
    + rulesFile, e);
    }
    }
    ...

    在清單 4 中可以看到,RulesEngine 類的構(gòu)造函數(shù)接受字符串值形式的參數(shù),該值表示包含業(yè)務(wù)規(guī)則集合的文件的名稱。該構(gòu)造函數(shù)使用 RuleBaseLoader 類的靜態(tài) loadFromInputStream() 方法將規(guī)則文件中包含的規(guī)則加載到內(nèi)存中。(注意,該代碼假設(shè)規(guī)則文件位于程序類路徑中名為 rules 的文件夾中。)loadFromInputStream() 方法返回 Drools RuleBase 類的實(shí)例,它被分配給 RulesEngine 類的 rules 屬性??梢詫?RulesBase 類的實(shí)例看作規(guī)則文件中所包含規(guī)則的內(nèi)存中表示。

    清單 5 展示了 RulesEngine 類的 executeRules() 方法:


    清單 5. RulesEngine 類的 executeRules() 方法
    																				
    public List executeRules(WorkingEnvironmentCallback callback)
    throws RulesEngineException {

    try {
    WorkingMemory workingMemory = rules.newWorkingMemory();
    if (debug) {
    workingMemory.addEventListener(
    new DebugWorkingMemoryEventListener());
    }
    callback.initEnvironment(workingMemory);
    workingMemory.fireAllRules();
    return workingMemory.getObjects();
    } catch (FactException fe) {
    logFactException(fe);
    throw new RulesEngineException(
    "Exception occurred while attempting to execute "
    + "rules file: " + rulesFile, fe);
    }
    }

    executeRules()方法幾乎包含了 Java 代碼中的所有魔力。調(diào)用該方法執(zhí)行先前加載到類構(gòu)造函數(shù)中的規(guī)則。Drools WorkingMemory 類的實(shí)例用于斷言或聲明知識(shí),規(guī)則引擎應(yīng)使用它來(lái)確定應(yīng)執(zhí)行的結(jié)果。(如果滿足規(guī)則的所有條件,則執(zhí)行該規(guī)則的結(jié)果。)將知識(shí)當(dāng)作規(guī)則引擎用于確定是否應(yīng)啟動(dòng)規(guī)則的數(shù)據(jù)或信息。例如,規(guī)則引擎的知識(shí)可以包含一個(gè)或多個(gè)對(duì)象及其屬性的當(dāng)前狀態(tài)。

    規(guī)則結(jié)果的執(zhí)行在調(diào)用 WorkingMemory 對(duì)象的 fireAllRules() 方法時(shí)執(zhí)行。您可能奇怪(我希望您如此)知識(shí)是如何斷言到 WorkingMemory 實(shí)例中的。如果仔細(xì)看一下該方法的簽名,將會(huì)注意到所傳遞的參數(shù)是 WorkingEnvironmentCallback 接口的實(shí)例。executeRules() 方法的調(diào)用者需要?jiǎng)?chuàng)建實(shí)現(xiàn)該接口的對(duì)象。該接口只需要開(kāi)發(fā)人員實(shí)現(xiàn)一個(gè)方法(參見(jiàn)清單 6 ):


    清單 6. WorkingEnvironmentCallback 接口
    																				
    public interface WorkingEnvironmentCallback {
    void initEnvironment(WorkingMemory workingMemory) throws FactException;
    }

    所以,應(yīng)該是 executeRules() 方法的調(diào)用者將知識(shí)斷言到 WorkingMemory 實(shí)例中的。稍后將展示這是如何實(shí)現(xiàn)的。

    TestsRulesEngine 類

    清單 7 展示了 TestsRulesEngine 類,它也位于 demo 包中:


    清單 7. TestsRulesEngine 類
    																				
    public class TestsRulesEngine {

    private RulesEngine rulesEngine;
    private TestDAO testDAO;

    public TestsRulesEngine(TestDAO testDAO) throws RulesEngineException {
    super();
    rulesEngine = new RulesEngine("testRules.xml");
    this.testDAO = testDAO;
    }

    public void assignTests(final Machine machine) {
    rulesEngine.executeRules(new WorkingEnvironmentCallback() {
    public void initEnvironment(WorkingMemory workingMemory)
    throws FactException {

    workingMemory.assertObject(machine);
    Iterator functions = machine.getFunctions().iterator();
    while (functions.hasNext()) {
    workingMemory.assertObject(functions.next());
    }
    workingMemory.setApplicationData("testDAO", testDAO);
    };
    });
    }
    }

    TestsRulesEngine 類只有兩個(gè)實(shí)例變量。rulesEngine 屬性是 RulesEngine 類的實(shí)例。testDAO 屬性保存對(duì) TestDAO 接口的具體實(shí)現(xiàn)的引用。rulesEngine 對(duì)象使用 “testRules.xml” 字符串作為其構(gòu)造函數(shù)的參數(shù)來(lái)進(jìn)行實(shí)例化。testRules.xml 文件以聲明方式捕獲 要解決的問(wèn)題 中的業(yè)務(wù)規(guī)則。TestsRulesEngine 類的 assignTests() 方法調(diào)用 RulesEngine 類的 executeRules() 方法。在該方法中,創(chuàng)建了 WorkingEnvironmentCallback 接口的匿名實(shí)例,然后該實(shí)例被作為參數(shù)傳遞給 executeRules() 方法。

    如果查看 assignTests() 方法的實(shí)現(xiàn),可以看到知識(shí)是如何斷言到 WorkingMemory 實(shí)例中的。WorkingMemory 類的 assertObject() 方法被調(diào)用以聲明在對(duì)規(guī)則求值時(shí)規(guī)則引擎應(yīng)使用的知識(shí)。在這種情況下,知識(shí)由 Machine 類的實(shí)例和該機(jī)器的功能組成。被斷言的對(duì)象用于對(duì)規(guī)則的條件求值。

    如果在對(duì)條件求值時(shí),需要讓規(guī)則引擎引用 用作知識(shí)的對(duì)象,則應(yīng)使用 WorkingMemory 類的 setApplicationData() 方法。在示例程序中,setApplicationData() 方法將對(duì) TestDAO 實(shí)例的引用傳遞給規(guī)則引擎。然后規(guī)則引擎使用 TestDAO 查找它可能需要的任何 Test 實(shí)例。

    TestsRulesEngine 類是示例程序中惟一的 Java 代碼,它包含專門致力于為機(jī)器分配測(cè)試和測(cè)試到期日期的實(shí)現(xiàn)的邏輯。該類中的邏輯永遠(yuǎn)不需要更改,即使業(yè)務(wù)規(guī)則需要更新時(shí)。





    回頁(yè)首


    Drools 規(guī)則文件

    如前所述,testRules.xml 文件包含規(guī)則引擎為機(jī)器分配測(cè)試和測(cè)試到期日期所遵循的規(guī)則。它使用 Java/XML 語(yǔ)法表達(dá)所包含的規(guī)則。

    Drools 規(guī)則文件具有一個(gè)名為 rule-set 的根元素,它由一個(gè)或多個(gè) rule 元素組成。每個(gè) rule 規(guī)則由一個(gè)或多個(gè) parameter 元素、一個(gè)或多個(gè) condition 元素以及一個(gè) consequence 元素組成。rule-set 元素還可以具有一個(gè)或多個(gè) import 元素、一個(gè)或多個(gè) application-data 元素以及一個(gè) functions 元素。

    理解 Drools 規(guī)則文件組成最好的方法是查看一個(gè)真正的規(guī)則文件。下面來(lái)看 testRules.xml 文件的第一部分,如清單 8 所示:


    清單 8. testRules.xml 文件的第一部分
    <rule-set name="Tests assignment rules" xmlns="http://drools.org/rules"
    xmlns:java="http://drools.org/semantics/java"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <java:import>demo.Machine</java:import>
    <java:import>demo.Test</java:import>
    <java:import>demo.TestDAO</java:import>
    <java:import>java.util.Calendar</java:import>
    <java:import>java.sql.Timestamp</java:import>
    <java:import>java.lang.String</java:import>
    <application-data identifier="testDAO">TestDAO</application-data>
    ...

    在清單 8 中,可以看到根元素 rule-set 具有 name 屬性,用于標(biāo)識(shí)該規(guī)則集合。import 元素允許規(guī)則執(zhí)行引擎知道在哪里查找將在規(guī)則中使用的對(duì)象的類定義。application-data 元素允許規(guī)則引擎知道某個(gè)對(duì)象應(yīng)該可以從規(guī)則中訪問(wèn),但該對(duì)象不應(yīng)是用于對(duì)規(guī)則條件求值的知識(shí)的一部分。該元素具有 identifier 屬性,它應(yīng)該與調(diào)用 WorkingMemory 類的 setApplicationData() 方法時(shí)使用的 identifier 相匹配(參見(jiàn) 清單 7)。

    functions 元素可以包含一個(gè)或多個(gè) Java 函數(shù)的定義(參見(jiàn)清單 9)。如果看到 consequence 元素(稍后將討論)中重復(fù)的代碼,則應(yīng)該提取該代碼并將其編寫為 functions 元素中的一個(gè) Java 函數(shù)。但是,在使用該元素時(shí)要謹(jǐn)慎,因?yàn)槟鷳?yīng)該避免在 Drools 規(guī)則文件中編寫復(fù)雜的 Java 代碼。該元素中定義的 Java 函數(shù)應(yīng)該簡(jiǎn)短易懂。這不是 Drools 的技術(shù)限制。如果想要在規(guī)則文件中編寫復(fù)雜的 Java 代碼,也可以。但這樣做可能會(huì)讓您的代碼更加難以測(cè)試、調(diào)試和維護(hù)。復(fù)雜的 Java 代碼應(yīng)該是 Java 類的一部分。如果需要 Drools 規(guī)則執(zhí)行引擎調(diào)用復(fù)雜的 Java 代碼,則可以將對(duì)包含復(fù)雜代碼的 Java 類的引用作為應(yīng)用程序數(shù)據(jù)傳遞給規(guī)則引擎。


    清單 9. testRules.xml 文件中定義的 Java 函數(shù)
    																				
    <java:functions>
    public static void setTestsDueTime(Machine machine, int numberOfDays) {
    setTestsDueTime(machine, Calendar.DATE, numberOfDays);
    }
    public static void setTestsDueTime(Machine machine, int field, int amount) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(machine.getCreationTs());
    calendar.add(field, amount);
    machine.setTestsDueTime(new Timestamp(calendar.getTimeInMillis()));
    }
    </java:functions>
    ...

    清單 10 展示了在 testRules.xml 文件中找到的第一個(gè)規(guī)則:


    清單 10. testRules.xml 文件中定義的第一個(gè)規(guī)則
    																				
    <rule name="Tests for type1 machine" salience="100">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <java:condition>machine.getType().equals("Type1")</java:condition>
    <java:consequence>
    Test test1 = testDAO.findByKey(Test.TEST1);
    Test test2 = testDAO.findByKey(Test.TEST2);
    Test test5 = testDAO.findByKey(Test.TEST5);
    machine.getTests().add(test1);
    machine.getTests().add(test2);
    machine.getTests().add(test5);
    drools.assertObject(test1);
    drools.assertObject(test2);
    drools.assertObject(test5);
    </java:consequence>
    </rule>

    如清單 10 所示,rule 元素具有惟一標(biāo)識(shí) rule-setrulename 屬性。可以看到清單 10 中顯示的規(guī)則只接受一個(gè)參數(shù):Machine 對(duì)象。如果返回 清單 7,將會(huì)看到 Machine 對(duì)象被斷言到 WorkingMemory 對(duì)象中。同一對(duì)象被作為參數(shù)傳遞給該規(guī)則。condition 元素對(duì) Machine 實(shí)例(知識(shí)的一部分)求值以確定是否應(yīng)執(zhí)行規(guī)則的結(jié)果。如果條件等于 true,則啟動(dòng)或執(zhí)行結(jié)果。consequence 元素具有一個(gè)或多個(gè) Java 語(yǔ)言語(yǔ)句。通過(guò)快速瀏覽該規(guī)則,可以很容易地識(shí)別出這是下列業(yè)務(wù)規(guī)則的實(shí)現(xiàn):

    • 如果計(jì)算機(jī)是 Type1,則只能在該機(jī)器上執(zhí)行 Test1、Test2 和 Test5。

    可能看起來(lái)有點(diǎn)怪的語(yǔ)句只有最后三條 Java 語(yǔ)句,它們是 consequence 元素的一部分?;貞?要解決的問(wèn)題 中的業(yè)務(wù)規(guī)則,應(yīng)分配給測(cè)試到期日期的值取決于分配給機(jī)器的測(cè)試。所以分配給機(jī)器的測(cè)試需要成為對(duì)規(guī)則求值時(shí)規(guī)則執(zhí)行引擎應(yīng)使用的知識(shí)的一部分。這正是 consequence 元素中最后三條語(yǔ)句要做的事情。這些語(yǔ)句使用一個(gè)名為 drools 的變量(可用于任何結(jié)果塊中)以更新規(guī)則引擎中的知識(shí)。

    確定規(guī)則執(zhí)行順序

    規(guī)則的另一個(gè)重要的方面是可選的 salience 屬性。使用它可以讓規(guī)則執(zhí)行引擎知道應(yīng)該啟動(dòng)規(guī)則集合中規(guī)則的結(jié)果塊的順序。具有最高顯著值的規(guī)則的結(jié)果塊首先執(zhí)行;具有第二高顯著值的規(guī)則的結(jié)果塊第二執(zhí)行,依此類推。當(dāng)您需要讓規(guī)則按預(yù)定義順序啟動(dòng)時(shí),這一點(diǎn)非常重要,很快您將會(huì)看到。

    testRules.xml 文件中接下來(lái)的四個(gè)規(guī)則實(shí)現(xiàn)與機(jī)器測(cè)試分配有關(guān)的其他業(yè)務(wù)規(guī)則(參見(jiàn)清單 11)。這些規(guī)則與剛討論的第一個(gè)規(guī)則非常相似。注意,salience 屬性值對(duì)于前五個(gè)規(guī)則是相同的;不管這五個(gè)規(guī)則的啟動(dòng)順序如何,其執(zhí)行結(jié)果將相同。如果結(jié)果受規(guī)則的啟動(dòng)順序影響,則需要為規(guī)則指定不同的顯著值。


    清單 11. testRules.xml 文件中與測(cè)試分配有關(guān)的其他規(guī)則
    																				
    <rule name="Tests for type2, DNS server machine" salience="100">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="function">
    <java:class>
    String
    </java:class>
    </parameter>
    <java:condition>machine.getType().equals("Type2")</java:condition>
    <java:condition>function.equals("DNS Server")</java:condition>
    <java:consequence>
    Test test5 = testDAO.findByKey(Test.TEST5);
    Test test4 = testDAO.findByKey(Test.TEST4);
    machine.getTests().add(test5);
    machine.getTests().add(test4);
    drools.assertObject(test4);
    drools.assertObject(test5);
    </java:consequence>
    </rule>

    <rule name="Tests for type2, DDNS server machine" salience="100">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="function">
    <java:class>
    String
    </java:class>
    </parameter>
    <java:condition>machine.getType().equals("Type2")</java:condition>
    <java:condition>function.equals("DDNS Server")</java:condition>
    <java:consequence>
    Test test2 = testDAO.findByKey(Test.TEST2);
    Test test3 = testDAO.findByKey(Test.TEST3);
    machine.getTests().add(test2);
    machine.getTests().add(test3);
    drools.assertObject(test2);
    drools.assertObject(test3);
    </java:consequence>
    </rule>

    <rule name="Tests for type2, Gateway machine" salience="100">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="function">
    <java:class>
    String
    </java:class>
    </parameter>
    <java:condition>machine.getType().equals("Type2")</java:condition>
    <java:condition>function.equals("Gateway")</java:condition>
    <java:consequence>
    Test test3 = testDAO.findByKey(Test.TEST3);
    Test test4 = testDAO.findByKey(Test.TEST4);
    machine.getTests().add(test3);
    machine.getTests().add(test4);
    drools.assertObject(test3);
    drools.assertObject(test4);
    </java:consequence>
    </rule>

    <rule name="Tests for type2, Router machine" salience="100">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="function">
    <java:class>
    String
    </java:class>
    </parameter>
    <java:condition>machine.getType().equals("Type2")</java:condition>
    <java:condition>function.equals("Router")</java:condition>
    <java:consequence>
    Test test3 = testDAO.findByKey(Test.TEST3);
    Test test1 = testDAO.findByKey(Test.TEST1);
    machine.getTests().add(test3);
    machine.getTests().add(test1);
    drools.assertObject(test1);
    drools.assertObject(test3);
    </java:consequence>
    </rule>
    ...

    清單 12 展示了 Drools 規(guī)則文件中的其他規(guī)則。您可能已經(jīng)猜到,這些規(guī)則與測(cè)試到期日期的分配有關(guān):


    清單 12. testRules.xml 文件中與測(cè)試到期日期分配有關(guān)的規(guī)則
    																				
    <rule name="Due date for Test 5" salience="50">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="test">
    <java:class>
    Test
    </java:class>
    </parameter>
    <java:condition>test.getId().equals(Test.TEST5)</java:condition>
    <java:consequence>
    setTestsDueTime(machine, 14);
    </java:consequence>
    </rule>

    <rule name="Due date for Test 4" salience="40">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="test">
    <java:class>
    Test
    </java:class>
    </parameter>
    <java:condition>test.getId().equals(Test.TEST4)</java:condition>
    <java:consequence>
    setTestsDueTime(machine, 12);
    </java:consequence>
    </rule>

    <rule name="Due date for Test 3" salience="30">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="test">
    <java:class>
    Test
    </java:class>
    </parameter>
    <java:condition>test.getId().equals(Test.TEST3)</java:condition>
    <java:consequence>
    setTestsDueTime(machine, 10);
    </java:consequence>
    </rule>

    <rule name="Due date for Test 2" salience="20">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="test">
    <java:class>
    Test
    </java:class>
    </parameter>
    <java:condition>test.getId().equals(Test.TEST2)</java:condition>
    <java:consequence>
    setTestsDueTime(machine, 7);
    </java:consequence>
    </rule>

    <rule name="Due date for Test 1" salience="10">
    <parameter identifier="machine">
    <java:class>
    Machine
    </java:class>
    </parameter>
    <parameter identifier="test">
    <java:class>
    Test
    </java:class>
    </parameter>
    <java:condition>test.getId().equals(Test.TEST1)</java:condition>
    <java:consequence>
    setTestsDueTime(machine, 3);
    </java:consequence>
    </rule>

    這些規(guī)則的實(shí)現(xiàn)與用于分配測(cè)試的規(guī)則的實(shí)現(xiàn)有一點(diǎn)相似,但我發(fā)現(xiàn)它們更有趣一些,原因有三。

    第一,注意這些規(guī)則的執(zhí)行順序很重要。結(jié)果(即,分配給 Machine 實(shí)例的 testsDueTime 屬性的值)受這些規(guī)則的啟動(dòng)順序所影響。如果查看 要解決的問(wèn)題 中詳細(xì)的業(yè)務(wù)規(guī)則,您將注意到用于分配測(cè)試到期日期的規(guī)則具有優(yōu)先順序。例如,如果已經(jīng)將 Test3、Test4 和 Test5 分配給機(jī)器,則測(cè)試到期日期應(yīng)距離機(jī)器的創(chuàng)建日期 10 天。原因在于 Test3 的到期日期規(guī)則優(yōu)先于 Test4 和 Test5 的測(cè)試到期日期規(guī)則。如果在 Drools 規(guī)則文件中表達(dá)這一點(diǎn)呢?答案是 salience 屬性。為 testsDueTime 屬性設(shè)置值的規(guī)則的 salience 屬性值不同。Test1 的測(cè)試到期日期規(guī)則優(yōu)先于所有其他測(cè)試到期日期規(guī)則,所以這應(yīng)是要啟動(dòng)的最后一個(gè)規(guī)則。換句話說(shuō),如果 Test1 是分配給機(jī)器的測(cè)試之一,則由該規(guī)則分配的值應(yīng)該是優(yōu)先使用的值。所以,該規(guī)則的 salience 值最低:10。

    第二,僅當(dāng) Test 類的實(shí)例成為知識(shí)的一部分(即,包含在工作內(nèi)存中)時(shí),才能對(duì)規(guī)則的 condition 元素求值,該元素用于為 testsDueTime 屬性分配值。這看起來(lái)非常合乎邏輯,因?yàn)槿绻?Test 類的實(shí)例不在工作內(nèi)存中,則規(guī)則執(zhí)行引擎無(wú)法執(zhí)行這些規(guī)則的條件中包含的比較。如果您想知道 Test 實(shí)例何時(shí)成為知識(shí)的一部分,那么回憶一下在執(zhí)行與測(cè)試分配有關(guān)的規(guī)則的結(jié)果塊時(shí),一個(gè)或多個(gè) Test 實(shí)例曾被斷言到工作內(nèi)存中(參見(jiàn) 清單 10清單 11)。

    第三,注意這些規(guī)則的結(jié)果塊相當(dāng)簡(jiǎn)短。原因在于在所有結(jié)果塊中調(diào)用了規(guī)則文件的 functions 元素中定義的 setTestsDueTime() Java 方法。該方法為 testsDueTime 屬性實(shí)際分配值。





    回頁(yè)首


    測(cè)試代碼

    既然已經(jīng)仔細(xì)檢查了實(shí)現(xiàn)業(yè)務(wù)規(guī)則邏輯的代碼,現(xiàn)在應(yīng)該檢查它是否能工作。要執(zhí)行示例程序,運(yùn)行 demo.test 包中的 TestsRulesEngineTest JUnit 測(cè)試。

    在該測(cè)試中,創(chuàng)建了 5 個(gè) Machine 對(duì)象,每個(gè)對(duì)象具有不同的屬性集合(序號(hào)、類型和功能)。為這五個(gè) Machine 對(duì)象的每一個(gè)都調(diào)用 TestsRulesEngine 類的 assignTests() 方法。一旦 assignTests() 方法完成其執(zhí)行,就執(zhí)行斷言以驗(yàn)證 testRules.xml 中指定的業(yè)務(wù)規(guī)則邏輯是否正確(參見(jiàn)清單 13)??梢孕薷?TestsRulesEngineTest JUnit 類以多添加幾個(gè)具有不同屬性的 Machine 實(shí)例,然后使用斷言驗(yàn)證結(jié)果是否跟預(yù)期一樣。


    清單 13. testTestsRulesEngine() 方法中用于驗(yàn)證業(yè)務(wù)邏輯實(shí)現(xiàn)是否正確的斷言
    																				
    public void testTestsRulesEngine() throws Exception {
    while (machineResultSet.next()) {
    Machine machine = machineResultSet.getMachine();
    testsRulesEngine.assignTests(machine);
    Timestamp creationTs = machine.getCreationTs();
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(creationTs);
    Timestamp testsDueTime = machine.getTestsDueTime();

    if (machine.getSerialNumber().equals("1234A")) {
    assertEquals(3, machine.getTests().size());
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST1)));
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST2)));
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST5)));
    calendar.add(Calendar.DATE, 3);
    assertEquals(calendar.getTime(), testsDueTime);

    } else if (machine.getSerialNumber().equals("1234B")) {
    assertEquals(4, machine.getTests().size());
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST5)));
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST4)));
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST3)));
    assertTrue(machine.getTests().contains(testDAO.findByKey(Test.TEST2)));
    calendar.add(Calendar.DATE, 7);
    assertEquals(calendar.getTime(), testsDueTime);
    ...





    回頁(yè)首


    關(guān)于知識(shí)的其他備注

    值得一提的是,除了將對(duì)象斷言至工作內(nèi)存之外,還可以在工作內(nèi)存中修改對(duì)象或從中撤回對(duì)象??梢栽谝?guī)則的結(jié)果塊中進(jìn)行這些操作。如果在結(jié)果塊中修改作為當(dāng)前知識(shí)一部分的對(duì)象,并且所修改的屬性被用在 condition 元素中以確定是否應(yīng)啟動(dòng)規(guī)則,則應(yīng)在結(jié)果塊中調(diào)用 drools 實(shí)例的 modifyObject() 方法。調(diào)用 modifyObject() 方法時(shí),您讓 Drools 規(guī)則引擎知道對(duì)象已更新且使用該對(duì)象的一個(gè)或多個(gè)屬性的任何條件(或任何規(guī)則)應(yīng)再次求值以確定條件的結(jié)果現(xiàn)在是 true 還是 false。這意味著甚至當(dāng)前活動(dòng)規(guī)則(在其結(jié)果塊中修改對(duì)象的規(guī)則)的條件都可以再次求值,這可能導(dǎo)致規(guī)則再次啟動(dòng),并可能導(dǎo)致無(wú)限循環(huán)。如果不希望這種情況發(fā)生,則應(yīng)該包括 rule 元素的可選 no-loop 屬性并將其賦值為 true

    清單 14 用兩個(gè)規(guī)則的定義的偽代碼演示了這種情況。Rule 1 修改 objectAproperty1。然后它調(diào)用 drools 變量的 modifyObject(),以允許規(guī)則執(zhí)行引擎知道該更新,從而觸發(fā)對(duì)引用 objectA 規(guī)則的 condition 元素的重新求值。因此,啟動(dòng) Rule 1 的條件應(yīng)再次求值。因?yàn)樵摋l件應(yīng)再次等于 trueproperty2 的值仍相同,因?yàn)樗诮Y(jié)果塊中未更改),Rule 1 應(yīng)再次啟動(dòng),從而導(dǎo)致無(wú)限循環(huán)的執(zhí)行。為了避免這種情況,添加 no-loop 屬性并將其賦值為 true,從而避免當(dāng)前活動(dòng)規(guī)則再次執(zhí)行。


    清單 14. 修改工作內(nèi)存中的對(duì)象并使用規(guī)則元素的 no-loop 屬性
    																				
    ...
    <rule name="Rule 1" salience="100" no-loop="true">
    <parameter identifier="objectA">
    <java:class>
    ClassA
    </java:class>
    </parameter>
    <java:condition>objectA.getProperty2().equals(...)</java:condition>
    <java:consequence>
    Object value = ...
    objectA.setProperty1(value);
    drools.modifyObject(objectA);
    </java:consequence>
    </rule>

    <rule name="Rule 2" salience="100">
    <parameter identifier="objectA">
    <java:class>
    ClassA
    </java:class>
    </parameter>
    <parameter identifier="objectB">
    <java:class>
    ClassB
    </java:class>
    </parameter>
    <java:condition>objectA.getProperty1().equals(objectB)</java:condition>
    ...
    <java:consequence>
    ...
    </java:consequence>
    </rule>
    ...

    如果對(duì)象不再是知識(shí)的一部分,則應(yīng)將該對(duì)象從工作內(nèi)存中撤回(參見(jiàn)清單 15)。通過(guò)在結(jié)果塊中調(diào)用 drools 對(duì)象的 retractObject() 方法實(shí)現(xiàn)這一點(diǎn)。當(dāng)從工作內(nèi)存中移除對(duì)象之后,引用該對(duì)象的(屬于任何規(guī)則的)任何 condition 元素將不被求值。因?yàn)閷?duì)象不再作為知識(shí)的一部分存在,所以規(guī)則沒(méi)有啟動(dòng)的機(jī)會(huì)。


    清單 15. 從工作內(nèi)存中撤回對(duì)象
    																				
    ...
    <rule name="Rule 1" salience="100" >
    <parameter identifier="objectA">
    <java:class>
    ClassA
    </java:class>
    </parameter>
    <parameter identifier="objectB">
    <java:class>
    ClassB
    </java:class>
    </parameter>
    <java:condition>...</java:condition>
    <java:condition>...</java:condition>
    <java:consequence>
    Object value = ...
    objectA.setProperty1(value);
    drools.retractObject(objectB);
    </java:consequence>
    </rule>

    <rule name="Rule 2" salience="90">
    <parameter identifier="objectB">
    <java:class>
    ClassB
    </java:class>
    </parameter>
    <java:condition>objectB.getProperty().equals(...)</java:condition>
    ...
    <java:consequence>
    ...
    </java:consequence>
    </rule>
    ...

    清單 15 包含兩個(gè)規(guī)則的定義的偽代碼。假設(shè)啟動(dòng)兩個(gè)規(guī)則的條件等于 true。則應(yīng)該首先啟動(dòng) Rule 1,因?yàn)?Rule 1 的顯著值比 Rule 2 的高?,F(xiàn)在,注意在 Rule 1 的結(jié)果塊中,objectB 從工作內(nèi)存中撤回(也就是說(shuō),objectB 不再是知識(shí)的一部分)。該動(dòng)作更改了規(guī)則引擎的 “執(zhí)行日程”,因?yàn)楝F(xiàn)在將不啟動(dòng) Rule 2。原因在于曾經(jīng)為真值的用于啟動(dòng) Rule 2 的條件不再為真,因?yàn)樗昧艘粋€(gè)不再是知識(shí)的一部分的對(duì)象(objectB)。如果清單 15 中還有其他規(guī)則引用了 objectB,且這些規(guī)則尚未啟動(dòng),則它們將不會(huì)再啟動(dòng)了。





    回頁(yè)首


    結(jié)束語(yǔ)

    使用規(guī)則引擎可以顯著降低實(shí)現(xiàn) Java 應(yīng)用程序中業(yè)務(wù)規(guī)則邏輯的組件的復(fù)雜性。使用規(guī)則引擎以聲明方法表達(dá)規(guī)則的應(yīng)用程序比不這樣做的應(yīng)用程序更有可能容易維護(hù)和擴(kuò)展。正如您所看到的, Drools 是一種功能強(qiáng)大的靈活的規(guī)則引擎實(shí)現(xiàn)。使用 Drools 的特性和能力,您應(yīng)該能夠以聲明方式實(shí)現(xiàn)應(yīng)用程序的復(fù)雜業(yè)務(wù)邏輯。Drools 使得學(xué)習(xí)和使用聲明編程對(duì)于 Java 開(kāi)發(fā)人員來(lái)說(shuō)相當(dāng)容易,因?yàn)樗哂?Java 語(yǔ)義模塊以允許使用 Java/XML 語(yǔ)法表達(dá)規(guī)則。

    本文展示的 Drools 類是特定于 Drools 的。如果要在示例程序中使用另一種規(guī)則引擎實(shí)現(xiàn),代碼需要作少許更改。因?yàn)?Drools 是 JSR 94 兼容的,所以可以使用 Java Rule Engine API(如 JSR 94 中所指定)設(shè)計(jì)特定于 Drools 的類的接口。(Java Rule Engine API 用于 JDBC 在數(shù)據(jù)庫(kù)中的規(guī)則引擎。)如果使用該 API,則可以無(wú)需更改 Java 代碼而將規(guī)則引擎實(shí)現(xiàn)更改為另一個(gè)不同的實(shí)現(xiàn),只要這個(gè)不同的實(shí)現(xiàn)也是 JSR 94 兼容的。JSR 94 不解析包含業(yè)務(wù)規(guī)則的規(guī)則文件(在本文示例應(yīng)用程序中為 testRules.xml)的結(jié)構(gòu)。文件的結(jié)構(gòu)將仍取決于您選擇的規(guī)則引擎。作為練習(xí),可以修改示例程序以使它使用 Java Rule Engine API,而不是使用 Java 代碼引用特定于 Drools 的類。






    回頁(yè)首


    下載

    描述 名字 大小 下載方法
    Sample Java project that uses Drools j-DroolsDemo.zip 5KB HTTP
    關(guān)于下載方法的信息 Get Adobe? Reader?




    回頁(yè)首
    posted on 2006-08-18 16:23 kongjia 閱讀(577) 評(píng)論(0)  編輯  收藏

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
     
    Copyright © kongjia Powered by: 博客園 模板提供:滬江博客
    主站蜘蛛池模板: 久久精品亚洲精品国产色婷| 亚洲人成电影青青在线播放| 青青草无码免费一二三区| 精品久久亚洲中文无码| 亚洲毛片av日韩av无码| 久久久久免费看成人影片| 亚洲欧美第一成人网站7777 | 亚洲精品国产自在久久| 免费人成视频在线观看网站| 在线视频亚洲一区| 亚洲一区二区影院| 免费va在线观看| 222www在线观看免费| 国产成人无码免费看片软件| 亚洲av永久无码嘿嘿嘿| 亚洲国产综合无码一区| 免费高清av一区二区三区| 国产高清不卡免费视频| 免费一级全黄少妇性色生活片| 亚洲婷婷综合色高清在线| 国产成人精品日本亚洲专区 | 在线观看免费无码专区| 国产AV无码专区亚洲AV麻豆丫| 亚洲最大黄色网址| 亚洲日产韩国一二三四区| 永久免费无码网站在线观看| 久久不见久久见免费视频7| fc2免费人成在线| 色噜噜的亚洲男人的天堂| 亚洲av成人综合网| 亚洲欧洲日韩不卡| 亚洲综合亚洲综合网成人| 日本不卡高清中文字幕免费| 亚洲免费综合色在线视频| 99久久国产免费-99久久国产免费| 国产精品免费视频观看拍拍| 国产午夜亚洲精品不卡电影| 亚洲自偷自偷在线成人网站传媒 | 77777亚洲午夜久久多人| 国产成人涩涩涩视频在线观看免费 | 亚洲精品自偷自拍无码|