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

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

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

    javajohn

    金色年華

    Drools 為你的業(yè)務(wù)邏輯提供框架[修訂]

    ???大部分 web 以及企業(yè)級(jí) Java 應(yīng)用可被分成三部分:與用戶交互的前臺(tái),與數(shù)據(jù)庫(kù)這樣的后臺(tái)系統(tǒng)交互的服務(wù)層,以及它們之間的業(yè)務(wù)邏輯。最近這段時(shí)間,通常我們會(huì)使用框架來(lái)實(shí)現(xiàn)前臺(tái)和后臺(tái)的需求(例如:Struts, Cocoon, Spring, Hibernate, JDO, 以及實(shí)體 Beans),但是卻沒(méi)有一種標(biāo)準(zhǔn)手段很好的組織業(yè)務(wù)邏輯。像 EJB 和 Spring 這樣的框架都以 high level 方式處理,這無(wú)助于組織我們的代碼。除非我們改變這種凌亂,否則系統(tǒng)將不會(huì)健壯,框架中雜亂的 if...then 語(yǔ)句能帶給我們可配置性、可讀性的優(yōu)點(diǎn),以及在其他地方復(fù)用代碼的愉悅嗎?本文將介紹如何使用 Drools 規(guī)則引擎框架來(lái)解決這些問(wèn)題。
    ???
    ??? 下列的范例代碼展示了我們正要試圖努力避免的問(wèn)題。展示了包含一些業(yè)務(wù)邏輯的 Java 典型應(yīng)用。

    if ?((user.isMemberOf(AdministratorGroup)
    ??????
    && ?user.isMemberOf(teleworkerGroup))
    ?????
    || ?user.isSuperUser(){
    ?????????
    ?????????
    // ?more?checks?for?specific?cases
    ????????? if ((expenseRequest.code().equals( " B203 " )
    ???????????
    || (expenseRequest.code().equals( " A903 " )
    ????????????????????????
    && (totalExpenses < 200 )
    ????????????????
    && (bossSignOff > ?totalExpenses))
    ???????????
    && (deptBudget.notExceeded))?{
    ???????????????
    // issue?payments
    ???????????}? else ? if ?{
    ???????????????
    // check?lots?of?other?conditions
    ???????????}
    }?
    else ?{
    ?????
    // ?even?more?business?logic
    }

    ??? 我們經(jīng)常寫(xiě)出類似的(甚至更復(fù)雜)業(yè)務(wù)邏輯。當(dāng)這些用 Java 實(shí)現(xiàn)的業(yè)務(wù)邏輯成為標(biāo)準(zhǔn)方式時(shí),將存在下列問(wèn)題:
    ???
    ??????? 業(yè)務(wù)用戶怎樣在這些難以理解的代碼基礎(chǔ)上添加另一個(gè)條件(比如"C987")?一旦最初開(kāi)發(fā)這些程序的員工離開(kāi)了,你想成為維護(hù)這些代碼的人嗎?
    ??????? 我們?cè)鯓訖z查規(guī)則的正確性?對(duì)業(yè)務(wù)伙伴的技術(shù)人員來(lái)說(shuō)都?jí)驈?fù)雜的了,更不要說(shuō)檢查。我們可以有系統(tǒng)的測(cè)試這些業(yè)務(wù)邏輯嗎?
    ??????? 很用應(yīng)用都有相似的業(yè)務(wù)規(guī)則--當(dāng)其中的一個(gè)規(guī)則改變,我們能保證這一改變可貫穿整個(gè)系統(tǒng)?當(dāng)新應(yīng)用使用這些規(guī)則,該應(yīng)用已經(jīng)部分添加了新的規(guī)則,但不完全,我們要把邏輯重寫(xiě)過(guò)嗎?
    ??????? 我們經(jīng)常需要對(duì)每個(gè)細(xì)小調(diào)整所帶來(lái)的改變進(jìn)行重編譯/重部署,而不是堅(jiān)實(shí)的依靠 Java 代碼,業(yè)務(wù)邏輯是否易于配置?
    ??????? 可否復(fù)用已存在的用其他(腳本)語(yǔ)言編寫(xiě)的業(yè)務(wù)規(guī)則邏輯?
    ???????
    ??? J2EE/EJB 以及“IoC inversion of control”框架(比如 Spring,Pico,以及 Avalon)給我們帶來(lái)的是 high level 代碼組織能力。在提供良好復(fù)用性、可配置性、以及安全性的同時(shí),沒(méi)有一個(gè)能替代(解決)以上的“spaghetti 代碼”范例出現(xiàn)的問(wèn)題。理想地,無(wú)論選擇何種框架,不僅僅適合 J2EE 應(yīng)用,而且也可用于“normal”Java(J2SE)程序,以及大部分普遍采用的表現(xiàn)層以及持久層框架。這種理想框架應(yīng)該允許我們這樣做:
    ???
    ??????? 業(yè)務(wù)用戶應(yīng)該可以方便的閱讀和校驗(yàn)業(yè)務(wù)邏輯。
    ??????? 業(yè)務(wù)規(guī)則應(yīng)該可被復(fù)用,并可以通過(guò)程序進(jìn)行配置。
    ??????? 這種框架應(yīng)該是可升級(jí)的,并在高負(fù)載情況下運(yùn)行。
    ??????? Java 程序員可以像使用現(xiàn)有的前臺(tái)(Struts,Spring)和后臺(tái)(ORM object-relational mapping)框架一樣方便地使用這個(gè)框架。
    ???????
    ??? 另外的問(wèn)題是,有許多的 web 頁(yè)面、數(shù)據(jù)庫(kù)訪問(wèn)組織方式,業(yè)務(wù)邏輯在這兩種應(yīng)用中應(yīng)趨于不同。而框架應(yīng)該能應(yīng)付這些并促進(jìn)代碼復(fù)用。理想的框架將能“frameworks all the way down.”,通過(guò)這種方式使用框架,我們能在應(yīng)用中大量的“out of the box”,這樣我們只為客戶記錄添加值的部分。
    ???
    規(guī)則引擎前來(lái)救援

    ??? 我們?cè)鯓咏鉀Q問(wèn)題呢?一種方案是通過(guò)規(guī)則引擎獲取 traction。規(guī)則引擎是為組織業(yè)務(wù)邏輯應(yīng)運(yùn)而生的框架,它讓開(kāi)發(fā)者專注于做被認(rèn)為正確的事情上,而不是以 low-level 方式作出決定。
    ???
    ??? 通常,業(yè)務(wù)用戶舒適的表達(dá)他們知道的正確的事,而不是 if...else 格式的表達(dá)方式。你也許能從業(yè)務(wù)專家聽(tīng)見(jiàn)這些東西:
    ???
    ??? “FORM 10A 用來(lái)索取額外 200 歐元費(fèi)用。(FORM 10A is used for expense claims over 200 Euro.)”
    ??? “我們只進(jìn)行數(shù)量在 10,000 以上的貿(mào)易。”
    ??? “購(gòu)買(mǎi)大于 €10m 的要經(jīng)過(guò)公司董事批準(zhǔn)。”
    ???
    ??? 通過(guò)專注于我們認(rèn)為正確的事情上,而不是只知道怎樣用 Java 代碼表達(dá),那么上面的敘述將比之前的代碼范例更清晰。我們?nèi)匀恍枰环N機(jī)制為我們知道和做決定的事實(shí)應(yīng)用這些規(guī)則。這種機(jī)制就是規(guī)則引擎。
    ???
    Java 中的規(guī)則引擎
    ???
    ??? JSR 94,如同 JBDC 允許我們與多種數(shù)據(jù)庫(kù)交互一樣,javax.rules 是一組與規(guī)則引擎交互的通用標(biāo)準(zhǔn) API。為什么 JSR-94 沒(méi)有詳細(xì)說(shuō)明實(shí)際的規(guī)則怎樣書(shū)寫(xiě),有下面大量的 Java 規(guī)則引擎可供選擇:
    ???
    ??? Jess 或許是最成熟的 Java 規(guī)則引擎,有良好的工具支持(包括 Eclipse 插件)以及文檔。但是,它是商業(yè)軟件,而且用 Prolog-style 符號(hào)書(shū)寫(xiě)規(guī)則,對(duì) Java 程序員來(lái)說(shuō)是很晦澀的。
    ??? Jena 是一套開(kāi)源框架,最初由惠普發(fā)起。雖然它有規(guī)則引擎以及在 Web 語(yǔ)義方面特別強(qiáng)大,但它并不與 JSR-94 兼容。
    ??? Drools 是與 JSR-94 兼容的規(guī)則引擎,并且在 Apache-style 許可下完全開(kāi)源。它不僅用熟悉的 Java 和 XML 語(yǔ)法表述規(guī)則,而且它還有強(qiáng)大的用戶、開(kāi)發(fā)者社區(qū)。在本文中有范例,我們將使用 Drools,因?yàn)樗凶钊菀资褂玫念愃?Java 的語(yǔ)法以及完全開(kāi)發(fā)許可。
    ???
    利用 Drools 開(kāi)始 Java 開(kāi)發(fā)

    ??? 假設(shè)有這樣的場(chǎng)景:在閱讀本文的數(shù)分鐘后,你老板要求你做一個(gè)股票交易應(yīng)用原型。這時(shí),業(yè)務(wù)用戶尚未被完全定義業(yè)務(wù)邏輯,你馬上會(huì)想到最好的辦法是用規(guī)則引擎實(shí)現(xiàn)。最終系統(tǒng)將可通過(guò)內(nèi)部網(wǎng)訪問(wèn),而且還要和后臺(tái)數(shù)據(jù)庫(kù)以及消息系統(tǒng)通訊。在著手行動(dòng)前,先下載 Drools 框架(與支持庫(kù)一起)。在你喜歡的 IDE 中創(chuàng)建新項(xiàng)目,確定所有 .jar 文件被引用進(jìn)項(xiàng)目,如圖 1 中所示。截圖是基于 Eclipse 的,不過(guò)在其他 IDE 中創(chuàng)建也是相似的。

    ????? Libraries needed to Run Drools
    ?????????????????? 圖 1. 運(yùn)行 Drools 所需要的庫(kù)(2.5版本可從http://dist.codehaus.org/drools/distributions/drools-2.5-final-bin-withdeps.zip下載包括支持庫(kù))

    ????
    ??? 如果我們的股票交易系統(tǒng)很混亂,將失去大量潛在客戶(商機(jī)),所以在系統(tǒng)的整個(gè)步驟中放入一些模擬器(simulator)是至關(guān)重要的。這種模擬器給了你決心采用該系統(tǒng)的信心,甚至規(guī)則改變以后所帶來(lái)的麻煩。我們將借助敏捷工具箱中的工具,以及 JUnit(http://www.junit.org/) 框架進(jìn)行模擬。
    ???
    ??? 如下,我們寫(xiě)的第一段代碼是 JUnit 測(cè)試/模擬器。即使我們無(wú)法測(cè)試每個(gè)對(duì)應(yīng)用有價(jià)值的輸入組合,但有測(cè)試也比沒(méi)有測(cè)試的好。在這個(gè)范例中,所有的文件和類(包括單元測(cè)試)都放入一個(gè)文件夾/包中,但實(shí)際上,你可能會(huì)用一種適當(dāng)?shù)陌⑽募A結(jié)構(gòu)。范例代碼中我們用 Log4j 代替 System.out 調(diào)用。

    import ?junit.framework.TestCase;
    /*
    ?*?JUnit?test?for?the?business?rules?in?the?
    ?*?application.
    ?*?
    ?*?This?also?acts?a?'simulator'?for?the?business?
    ?*?rules?-?allowing?us?to?specify?the?inputs,
    ?*?examine?the?outputs?and?see?if?they?match?our?
    ?*?expectations?before?letting?the?code?loose?in??
    ?*?the?real?world.
    ?
    */
    public ? class ?BusinessRuleTest? extends ?TestCase?{
    ??
    /**
    ??*?Tests?the?purchase?of?a?stock
    ??
    */
    ??
    public ? void ?testStockBuy()? throws ?Exception{
    ????????????????
    ????
    // Create?a?Stock?with?simulated?values
    ????StockOffer?testOffer? = ? new ?StockOffer();
    ????testOffer.setStockName(
    " MEGACORP " );
    ????testOffer.setStockPrice(
    22 );
    ????testOffer.setStockQuantity(
    1000 );
    ????????????????
    ????
    // Run?the?rules?on?it
    ????BusinessLayer.evaluateStockPurchase(testOffer);
    ????????????????
    ????
    // Is?it?what?we?expected?
    ????assertTrue(
    ??????testOffer.getRecommendPurchase()
    != null );
    ????
    ????assertTrue(
    " YES " .equals(
    ??????testOffer.getRecommendPurchase()));???????????????
    ???}
    }

    ??? 這是最基本的 JUnt 測(cè)試,我們知道我們的系統(tǒng)應(yīng)該買(mǎi)所有低于 100 歐元的股票。很明顯,要是沒(méi)有數(shù)據(jù)持有類(StockOffer.java)和業(yè)務(wù)層類(BusinessLayer.java)它將無(wú)法編譯。這兩個(gè)類如下。

    /**
    ?*?Facade?for?the?Business?Logic?in?our?example.
    ?*?
    ?*?In?this?simple?example,?all?our?business?logic
    ?*?is?contained?in?this?class?but?in?reality?it?
    ?*?would?delegate?to?other?classes?as?required.
    */
    public ? class ?BusinessLayer?{
    ??
    /**
    ???*?Evaluate?whether?or?not?it?is?a?good?idea
    ???*?to?purchase?this?stock.
    ???*?
    @param ?stockToBuy
    ???*?
    @return ?true?if?the?recommendation?is?to?buy?
    ???*???the?stock,?false?if?otherwise
    ???
    */
    ??
    public ? static ? void ?evaluateStockPurchase
    ????(StockOffer?stockToBuy){
    ????????????????
    return ? false ;
    ??}
    }

    ??? StockOffer 是這樣:

    /**
    ?*?Simple?JavaBean?to?hold?StockOffer?values.
    ?*?A?'Stock?offer'?is?an?offer?(from?somebody?else)
    ?*?to?sell?us?a?Stock?(or?Company?share).
    ?
    */
    public ? class ?StockOffer?{
    ????????
    ??
    // constants
    ?? public ? final ? static ?String?YES = " YES " ;
    ??
    public ? final ? static ?String?NO = " NO " ;
    ????????
    ??
    // Internal?Variables
    ?? private ?String?stockName? = null ;
    ??
    private ? int ?stockPrice = 0 ;
    ??
    private ? int ?stockQuantity = 0 ;
    ??
    private ?String?recommendPurchase? = ? null ;
    ????????
    ??
    /**
    ???*?
    @return ?Returns?the?stockName.
    ???
    */
    ??
    public ?String?getStockName()?{
    ????????
    return ?stockName;
    ??}
    ??
    /**
    ???*?
    @param ?stockName?The?stockName?to?set.
    ???
    */
    ??
    public ? void ?setStockName(String?stockName)?{
    ????????
    this .stockName? = ?stockName;
    ??}
    ??
    /**
    ???*?
    @return ?Returns?the?stockPrice.
    ???
    */
    ??
    public ? int ?getStockPrice()?{
    ????????
    return ?stockPrice;
    ??}
    ??
    /**
    ???*?
    @param ?stockPrice?The?stockPrice?to?set.
    ???
    */
    ??
    public ? void ?setStockPrice( int ?stockPrice)?{
    ????????
    this .stockPrice? = ?stockPrice;
    ??}
    ??
    /**
    ???*?
    @return ?Returns?the?stockQuantity.
    ???
    */
    ??
    public ? int ?getStockQuantity()?{
    ????????
    return ?stockQuantity;
    ??}
    ??
    /**
    ???*?
    @param ?stockQuantity?to?set.
    ???
    */
    ??
    public ? void ?setStockQuantity( int ?stockQuantity){
    ????????
    this .stockQuantity? = ?stockQuantity;
    ??}
    ??
    /**
    ???*?
    @return ?Returns?the?recommendPurchase.
    ???
    */
    ??
    public ?String?getRecommendPurchase()?{
    ????????
    return ?recommendPurchase;
    ??}
    ?public void setRecommendPurchase(String recommendPurchase) {
    ??this.recommendPurchase = recommendPurchase;
    ?}
    }

    ??? 通過(guò) IDE 的 JUnit 插件運(yùn)行 BusinessRuleTest。如果你不熟悉 JUnit,可在 JUnit 網(wǎng)站找到更多信息。不必驚訝,如圖 2 所示第二個(gè)斷言測(cè)試失敗,這是因?yàn)檫€沒(méi)把業(yè)務(wù)邏輯放在適當(dāng)?shù)牡胤健y(cè)試結(jié)果用高亮顯示了模擬器/單元測(cè)試所出現(xiàn)的問(wèn)題,這是很保險(xiǎn)的。

    ? JUnit Test Results
    ????????????? 圖 2. JUnit 測(cè)試結(jié)果
    ?????????????
    用規(guī)則編寫(xiě)業(yè)務(wù)邏輯
    ???
    ??? 在這里,我們要寫(xiě)一些業(yè)務(wù)邏輯,來(lái)表達(dá)“一旦股票價(jià)格低于 100 歐元,就馬上購(gòu)買(mǎi)。” 要達(dá)到這個(gè)目的,需調(diào)整 BusinessLayer.java:

    import ?java.io.IOException;
    import ?org.drools.DroolsException;
    import ?org.drools.RuleBase;
    import ?org.drools.WorkingMemory;
    import ?org.drools.event.DebugWorkingMemoryEventListener;
    import ?org.drools.io.RuleBaseLoader;
    import ?org.xml.sax.SAXException;
    /**
    ?*?Facade?for?the?Business?Logic?in?our?example.
    ?*?
    ?*?In?this?simple?example,?all?our?business?logic
    ?*?is?contained?in?this?class?but?in?reality?it?
    ?*?would?delegate?to?other?classes?as?required.
    ?*?
    @author ?default
    ?
    */
    public ? class ?BusinessLayer?{
    ??
    // Name?of?the?file?containing?the?rules
    ?? private ? static ? final ?String?BUSINESS_RULE_FILE =
    ??????????????????????????????
    " BusinessRules.drl " ;
    ????????
    ??
    // Internal?handle?to?rule?base
    ?? private ? static ?RuleBase?businessRules? = ? null ;
    ??
    /**
    ???*?Load?the?business?rules?if?we?have?not?
    ???*?already?done?so.
    ???*?
    @throws ?Exception?-?normally?we?try?to?
    ???*??????????recover?from?these
    ???
    */
    ??
    private ? static ? void ?loadRules()
    ???????????????????????
    throws ?Exception{
    ????
    if ?(businessRules == null ){
    ??????businessRules?
    = ?RuleBaseLoader.loadFromUrl(
    ??????????BusinessLayer.
    class .getResource(
    ??????????BUSINESS_RULE_FILE?)?);
    ????}
    ??}?????
    ????????
    ??
    /**
    ???*?Evaluate?whether?or?not?to?purchase?stock.
    ???*?
    @param ?stockToBuy
    ???*?
    @return ?true?if?the?recommendation?is?to?buy
    ???*?
    @throws ?Exception
    ???
    */
    ??
    public ? static ? void ?evaluateStockPurchase
    ???????(StockOffer?stockToBuy)?
    throws ?Exception{
    ????????????????
    ????
    // Ensure?that?the?business?rules?are?loaded
    ????loadRules();
    ????
    // Some?logging?of?what?is?going?on
    ????System.out.println(? " FIRE?RULES " ?);
    ????System.out.println(?
    " ---------- " ?);
    ????????
    ????
    // Clear?any?state?from?previous?runs?
    ????WorkingMemory?workingMemory?
    ????????????
    = ?businessRules.newWorkingMemory();
    ????
    // Small?ruleset,?OK?to?add?a?debug?listener?
    ????workingMemory.addEventListener(
    ??????
    new ?DebugWorkingMemoryEventListener());
    ????????
    ????
    // Let?the?rule?engine?know?about?the?facts
    ????workingMemory.assertObject(stockToBuy);
    ????????
    ????
    // Let?the?rule?engine?do?its?stuff!!
    ????workingMemory.fireAllRules();
    ??}
    }

    ??? 這個(gè)類有些重要方法:
    ???
    ??? loadRules(),從 BusinessRules.drl 文件加載規(guī)則。
    ??? 更新后的 evaluateStockPurchase(),用于評(píng)估業(yè)務(wù)規(guī)則。這個(gè)方法的注解如下:
    ??????? 可以反復(fù)復(fù)用相同的 RuleSet(內(nèi)存中的業(yè)務(wù)規(guī)則是無(wú)狀態(tài)的)。
    ??????? 為每次評(píng)估構(gòu)造新的 WorkingMemory,因?yàn)槲覀兊闹R(shí)知道這個(gè)時(shí)刻是正確的。使用 assertObject() 放置已知事實(shí)(作為 Java 對(duì)象)到內(nèi)存中。
    ??????? Drools 有個(gè)事件監(jiān)聽(tīng)模式,允許我們“查看”事件模型中到底發(fā)生了什么。在這里我們用它打印 debug 信息。
    ??????? working memory 類中的 fireAllRules() 方法評(píng)估和更新規(guī)則(在本例中是股票出價(jià))。
    ???
    ??? 再次運(yùn)行該范例前,需要?jiǎng)?chuàng)建我們的 BusinessRules.drl 文件:

    <? xml?version="1.0" ?>
    < rule-set? name ="BusinessRulesSample"
    ??xmlns
    ="http://drools.org/rules"
    ??xmlns:java
    ="http://drools.org/semantics/java"
    ??xmlns:xs
    ????
    ="http://www.w3.org/2001/XMLSchema-instance"
    ??xs:schemaLocation
    ????
    ="http://drools.org/rules?rules.xsd
    ??http://drools.org/semantics/java?java.xsd"
    >
    ??
    <!-- ?Import?the?Java?Objects?that?we?refer?
    ??????????????????????????to?in?our?rules?
    --> ????????
    ? <java:import>
    ??? java.lang.Object
    ? </java:import>
    ? <java:import>
    ??? java.lang.String
    ? </java:import>
    ? <java:import>
    ??? [package path].StockOffer
    ? </java:import>
    ? <!-- A Java (Utility) function we reference in our rules-->?
    ? <java:functions>
    ??? public static void printStock(
    ????? [package path].StockOffer stock)
    ??????? {
    ??????? System.out.println("Name:"
    ????????? +stock.getStockName()
    ????????? +" Price: "+stock.getStockPrice()????
    ????????? +" BUY:"
    ????????? +stock.getRecommendPurchase());
    ??????? }
    ? </java:functions>

    ? <!-- Ensure stock price is not too high-->?????
    ? <rule name="Stock Price Low Enough">
    ??? <!-- Params to pass to business rule -->
    ??? <parameter identifier="stockOffer">
    ????? <java:class>[package path].StockOffer</java:class>
    ??? </parameter>
    ??? <!-- Conditions or 'Left Hand Side' (LHS) that must be met for business rule to fire -->
    ??? <!-- note markup -->
    ??? <java:condition>
    ????? stockOffer.getRecommendPurchase() == null
    ??? </java:condition>
    ??? <java:condition>
    ????? stockOffer.getStockPrice() &lt;(注:.drl文件內(nèi)大、小于號(hào)要使用&gt;和&lt;表示) 100
    ??? </java:condition>
    ??? <!-- What happens when the business rule is activated -->
    ??? <java:consequence>
    ??????? stockOffer.setRecommendPurchase([package path].StockOffer.YES);?
    ??????? printStock(stockOffer);
    ??? </java:consequence>
    ? </rule>
    </ rule-set >

    ??? 該規(guī)則文件有些有趣部分:
    ???
    ??????? 只有在 XML-Schema 定義 Java 對(duì)象之后,我們才能引用進(jìn)規(guī)則。這些對(duì)象可以是來(lái)自于任何必須的 Java 類庫(kù)。
    ??????? 接下來(lái)是 functions,它們可以與標(biāo)準(zhǔn) Java 代碼進(jìn)行混合。既然這樣,我們干脆混入些日志功能來(lái)幫助我們觀察發(fā)生了什么。
    ??????? 再下來(lái)是我們的 rule set,rule set 由一到多個(gè)規(guī)則組成。
    ??????? 每個(gè)規(guī)則可持有參數(shù)(StockOffer 類),并需要實(shí)現(xiàn)一個(gè)或多個(gè)條件,當(dāng)條件符合時(shí),將會(huì)執(zhí)行相應(yīng)結(jié)果。
    ???????
    ??? 在修改和編譯完代碼后,再次運(yùn)行 JUnit 測(cè)試。這次調(diào)用了業(yè)務(wù)規(guī)則,我們的邏輯進(jìn)行正確地評(píng)估,并且測(cè)試通過(guò),參看圖 3。恭喜--你已經(jīng)構(gòu)建了第一個(gè)基于規(guī)則的應(yīng)用!

    ??? Successful JUnit Test
    ??? 圖 3.成功的 JUnit 測(cè)試
    ???
    使規(guī)則更聰明

    ??? 剛剛構(gòu)建好應(yīng)用,你就向業(yè)務(wù)用戶示范上面的原型,他們卻忽然想起先前并沒(méi)有提出的規(guī)則。其中一個(gè)新規(guī)則是當(dāng)數(shù)量是負(fù)數(shù)時(shí)(<0)不能進(jìn)行股票交易。“沒(méi)關(guān)系,”你說(shuō),接著回到辦公桌上,緊扣已有知識(shí),快速演化你的系統(tǒng)。
    ???
    ??? 首先要更新模擬器,把以下代碼添加到 BusinessRuleTest.java:

    ?? /**
    ???*?Tests?the?purchase?of?a?stock?
    ???*?makes?sure?the?system?will?not?accept?
    ???*?negative?numbers.
    ???
    */
    ??
    public ? void ?testNegativeStockBuy()?
    ????????????????????????????????
    throws ?Exception{
    ????????????????
    ????
    // Create?a?Stock?with?our?simulated?values
    ??????StockOffer?testOffer? = ? new ?StockOffer();
    ????????testOffer.setStockName(
    " MEGACORP " );
    ????????testOffer.setStockPrice(
    - 22 );
    ????????testOffer.setStockQuantity(
    1000 );
    ????????????????
    ????????
    // Run?the?rules?on?it
    ????????BusinessLayer
    ??????????????.evaluateStockPurchase(testOffer);
    ????????????????
    ????????
    // Is?it?what?we?expected?
    ????????assertTrue( " NO " .equals(
    ??????????testOffer.getRecommendPurchase()));
    }

    ??? 這個(gè)測(cè)試是為業(yè)務(wù)用戶描述的新規(guī)則建立的。正如意料之中的,如果運(yùn)行 JUnit 測(cè)試,我們的新測(cè)試將失敗。所以,我們要添加新的規(guī)則到 .drl 文件:

    <!-- ?Ensure?that?negative?prices?
    ????????????????????????????are?not?accepted
    --> ??????
    ??
    < rule? name ="Stock?Price?Not?Negative" >
    ????
    <!-- ?Parameters?we?can?pass?into?
    ??????????????????????????the?business?rule?
    -->
    ????
    < parameter? identifier ="stockOffer" >
    ??????
    < class > StockOffer </ class >
    ????
    </ parameter >
    ????
    <!-- ?Conditions?or?'Left?Hand?Side'?(LHS)?
    ???????that?must?be?met?for?rule?to?fire?
    -->
    ????
    < java:condition >
    ??????stockOffer.getStockPrice()?
    < ?0
    ????
    </java:condition >
    ????
    <!-- ?What?happens?when?the?business?rule?
    ??????????????????????????????is?activated?
    -->
    ????
    < java:consequence >
    ??????stockOffer.setRecommendPurchase(
    ??????????????????????????????????StockOffer.NO);???????
    ??????printStock(stockOffer);
    ????
    </ java:consequence >
    ??
    </ rule >

    ??? 這個(gè)規(guī)則的格式和前面的相似,除了 (用于測(cè)試負(fù)數(shù))以及 用于設(shè)置推薦購(gòu)買(mǎi)為 No 以外。我們?cè)俅芜\(yùn)行測(cè)試,這次通過(guò)了。
    ???
    ??? 這時(shí),如果你習(xí)慣于過(guò)程化編程(像大多數(shù) Java 程序員一樣),你也許要搔頭皮了:在一個(gè)文件中包含兩個(gè)獨(dú)立的業(yè)務(wù)規(guī)則,而且我們也沒(méi)告訴規(guī)則引擎哪個(gè)更重要。不管怎樣,股票價(jià)格(對(duì)于 -22)都滿足兩個(gè)規(guī)則(也就是少于 0 和少于 100)。盡管這樣,我們?nèi)阅艿玫秸_結(jié)果,即使交換規(guī)則順序。這是怎么做到的呢?
    ???
    ??? 下面的控制臺(tái)輸出有助于我們了解到底怎么回事。我們看見(jiàn)兩個(gè)規(guī)則都執(zhí)行了([activationfired] 這行),Recommend Buy 第一次被設(shè)置為 Yes 接著又被設(shè)置成 No。Drools 怎么知道執(zhí)行這些規(guī)則的正確順序呢?如果你觀察 Stock Price Low Enough 規(guī)則,將發(fā)現(xiàn) recommendPurchase() 其中一個(gè)條件為空。通過(guò)這點(diǎn),Drools 規(guī)則引擎足以判斷 Stock Price Low Enough 規(guī)則應(yīng)該在 Stock Price Not Negative 規(guī)則之前執(zhí)行。這個(gè)過(guò)程稱為 conflict resolution。

    FIRE RULES
    ----------
    [ConditionTested: rule=Stock Price Not Negative;
    ? condition=[Condition: stockOffer.getStockPrice()
    ? < 0]; passed=true; tuple={[]}]
    [ActivationCreated: rule=Stock Price Not Negative;
    ? tuple={[]}]
    [ObjectAsserted: handle=[fid:2];
    ??
    object=net.firstpartners.rp.StockOffer@16546ef]
    [ActivationFired: rule=Stock Price Low Enough;
    ?? tuple={[]}]
    [ActivationFired: rule=Stock Price Not Negative;
    ?? tuple={[]}]
    Name:MEGACORP Price: -22 BUY:YES
    Name:MEGACORP Price: -22 BUY:NO


    ??? 如果你是一名過(guò)程化程序員,無(wú)論你用怎樣聰明的方式考慮這些,你都不會(huì)完全相信。這就是為什么要進(jìn)行單元/模擬器測(cè)試的原因:進(jìn)行 "堅(jiān)固的" JUnit 測(cè)試(使用一般 Java 代碼)確保規(guī)則引擎所作出的決定是按照我們所想要的路線進(jìn)行。(不會(huì)花費(fèi)大量金錢(qián)在無(wú)價(jià)值的股票上)同時(shí),規(guī)則引擎的強(qiáng)大和伸縮性允許我們快速開(kāi)發(fā)業(yè)務(wù)邏輯。
    ???
    ??? 稍后,我們將學(xué)習(xí)如何用更加精練的解決方案進(jìn)行沖突處理。
    ???
    沖突結(jié)局方案

    ??? 現(xiàn)在業(yè)務(wù)伙伴被打動(dòng)了,并且開(kāi)始考慮進(jìn)行選擇了。隨即他們遇到了個(gè) XYZ 公司股票的問(wèn)題,那么我們來(lái)實(shí)現(xiàn)新規(guī)則吧:只有 XYZ 公司股票低于 10 歐元才可購(gòu)買(mǎi)。
    ???
    ??? 像以前一樣,添加測(cè)試到模擬器,接著在規(guī)則文件中包含新業(yè)務(wù)規(guī)則。首先在 BusinessRuleTest.java 中添加新方法:

    ? /**
    ?*?Makes?sure?the?system?will?buy?stocks?
    ?*?of?XYZ?corp?only?if?it?really?cheap
    ?
    */
    public ? void ?testXYZStockBuy()? throws ?Exception{
    ????????
    ??
    // Create?a?Stock?with?our?simulated?values
    ??StockOffer?testOfferLow? = ? new ?StockOffer();
    ??StockOffer?testOfferHigh?
    = ? new ?StockOffer();
    ????????????????
    ??testOfferLow.setStockName(
    " XYZ " );
    ??testOfferLow.setStockPrice(
    9 );
    ??testOfferLow.setStockQuantity(
    1000 );
    ????????????????
    ??testOfferHigh.setStockName(
    " XYZ " );
    ??testOfferHigh.setStockPrice(
    11 );
    ??testOfferHigh.setStockQuantity(
    1000 );
    ????????????????
    ??
    // Run?the?rules?on?it?and?test
    ??BusinessLayer.evaluateStockPurchase(
    ????testOfferLow);
    ??assertTrue(
    " YES " .equals(
    ????testOfferLow.getRecommendPurchase()));
    ????????????????
    ??BusinessLayer.evaluateStockPurchase(
    ????testOfferHigh);
    ??assertTrue(
    " NO " .equals(
    ????testOfferHigh.getRecommendPurchase()));?????????????
    }

    ??? 接下來(lái)向 BusinessRules.drl 中添加新

    ?? < rule? name ="XYZCorp" ?salience ="-1" >
    ???
    <!-- ?Parameters?we?pass?to?rule? -->
    ???
    < parameter? identifier ="stockOffer" >
    ?????
    < class > StockOffer </ class >
    ???
    </ parameter >
    ????
    ???
    < java:condition >
    ?????stockOffer.getStockName().equals("XYZ")
    ???
    </ java:condition > ?
    ???
    < java:condition >
    ?????stockOffer.getRecommendPurchase()?==?null
    ???
    </ java:condition >
    ???
    < java:condition >
    ?????stockOffer.getStockPrice()?>?10
    ???
    </ java:condition >
    ????????
    ???
    <!-- ?What?happens?when?the?business?
    ????????????????????????????????rule?is?activated?
    -->
    ???
    < java:consequence > ?
    ?????stockOffer.setRecommendPurchase(
    ???????StockOffer.NO);??
    ?????printStock(stockOffer);
    ???
    </ java:consequence >
    ??
    </ rule >

    ??? 注意業(yè)務(wù)規(guī)則文件,在 rule name 后面,我們把 salience 設(shè)置成 -1(到目前為止了解的最低優(yōu)先級(jí))。大多數(shù)規(guī)則在系統(tǒng)中是沖突的,這意味著 Drools 必須為規(guī)則的執(zhí)行順序做判斷,假設(shè)這些條件都與規(guī)則匹配。默認(rèn)的判斷方式是:
    ???????
    ??? Salience:賦予的值。
    ??? Recency:使用規(guī)則的次數(shù)。
    ??? Complexity:首先執(zhí)行有復(fù)雜值的特定規(guī)則。
    ??? LoadOrder:規(guī)則載入的順序。
    ???
    ??? 如果沒(méi)有顯示的在規(guī)則中詳細(xì)指明,將會(huì)發(fā)生:
    ???
    ??? XYZ 公司規(guī)則("當(dāng)價(jià)格高于 10 歐元就不購(gòu)買(mǎi) XYZ 的股票")將先執(zhí)行(Recommend Buy 標(biāo)志被設(shè)置為 No)。
    ??? 接著更多的一般規(guī)則("購(gòu)買(mǎi)所有 100 歐元以下的股票")被執(zhí)行,把 Recommend Buy 標(biāo)志設(shè)置為 yes。
    ???
    ??? 這會(huì)給我們一個(gè)不想要的結(jié)果。然而,一旦在范例中設(shè)置了 saliency 要素,最終的測(cè)試和業(yè)務(wù)規(guī)則將像預(yù)期的那樣順利運(yùn)行。
    ???
    ??? 大多數(shù)時(shí)間,編寫(xiě)清晰的規(guī)則和設(shè)置 saliency 將給 Drools 足夠信息以選擇合適的順序執(zhí)行規(guī)則,有時(shí)我們想改變整個(gè)規(guī)則沖突處理方式。下面的例子說(shuō)明了如何改變,告訴規(guī)則引擎首先執(zhí)行最簡(jiǎn)單的規(guī)則。要注意的是:改變沖突解決方案要小心,它可能從根本上改變規(guī)則引擎的行為。

    ?? // Generate?our?list?of?conflict?resolvers
    ??ConflictResolver[]?conflictResolvers? = ?
    ????
    new ?ConflictResolver[]?{
    ??????SalienceConflictResolver.getInstance(),
    ??????RecencyConflictResolver.getInstance(),
    ????????SimplicityConflictResolver.getInstance(),
    ????????LoadOrderConflictResolver.getInstance()
    ????};
    ????????????????
    ??
    // Wrap?this?up?into?one?composite?resolver
    ??CompositeConflictResolver?resolver? = ?
    ????
    new ?CompositeConflictResolver(
    ??????conflictResolvers);
    ????????????????????????
    ??
    // Specify?this?resolver?when?we?load?the?rules
    ??businessRules? = ?RuleBaseLoader.loadFromUrl(
    ????BusinessLayer.
    class .getResource(?
    ??????BUSINESS_RULE_FILE),resolver);

    ??? 我們的簡(jiǎn)單應(yīng)用由 JUnit 測(cè)試驅(qū)動(dòng),我們不必改變 Drools 處理規(guī)則沖突的方式。知道沖突解決方案怎樣運(yùn)作是很有用的,尤其當(dāng)你的應(yīng)用為了迎合更復(fù)雜、更苛刻的需求時(shí)。
    ???
    結(jié)束
    ???
    ??? 本文示范了大部分程序員不得不面對(duì)的問(wèn)題:怎樣安排復(fù)雜業(yè)務(wù)邏輯的順序。我們示范了一個(gè)使用 Drools 作為解決方案并引入基于規(guī)則編程概念的簡(jiǎn)單應(yīng)用,包括了怎樣在運(yùn)行時(shí)處理規(guī)則。接著,后續(xù)文章使用這些技術(shù)并展示了怎樣在企業(yè)級(jí) Java 應(yīng)用中使用。

    原文地址:http://www.onjava.com/pub/a/onjava/2005/08/03/drools.html(第一次看到外國(guó)人的文檔寫(xiě)得這么不負(fù)責(zé)任)
    原譯者:Rosen Jiang 以及出處:http://www.tkk7.com/rosen
    本文僅供學(xué)習(xí),不得隨意引用、轉(zhuǎn)貼

    posted on 2006-05-27 18:50 javajohn 閱讀(1517) 評(píng)論(0)  編輯  收藏 所屬分類: 其他類

    My Links

    Blog Stats

    常用鏈接

    留言簿(7)

    隨筆分類(36)

    隨筆檔案(39)

    classmate

    good blog

    企業(yè)管理網(wǎng)站

    好友

    站點(diǎn)收藏

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 国产亚洲精品成人久久网站| 亚洲人成图片网站| 亚洲视频在线观看地址| 久久久久亚洲精品日久生情| 亚洲免费在线视频播放| 亚洲狠狠婷婷综合久久| www.av在线免费观看| 久久免费视频精品| 毛片免费观看的视频在线| 免费a级毛片大学生免费观看| 亚洲精品国产字幕久久不卡| 久久精品国产亚洲AV麻豆网站| 精品国产成人亚洲午夜福利| 黄页网址在线免费观看| 爱丫爱丫影院在线观看免费| 1000部拍拍拍18勿入免费凤凰福利 | 皇色在线免费视频| 最近中文字幕完整免费视频ww| 拍拍拍又黄又爽无挡视频免费| 亚洲男人天堂2020| 亚洲成a人片在线观看播放| 亚洲AV无码专区在线厂| 成人久久免费网站| 免费视频中文字幕| 亚洲国产精品无码专区影院| 亚洲免费福利在线视频| 黄色网页在线免费观看| 国产桃色在线成免费视频| 一级毛片直播亚洲| 久久精品国产亚洲AV嫖农村妇女| 国产成人亚洲午夜电影| 99热这里只有精品免费播放| 国产三级免费电影| 亚洲第一精品在线视频| 国产午夜亚洲精品不卡| 最近2019中文字幕免费直播| 亚洲国产成人久久笫一页| 亚洲成a人片在线观看播放| 亚洲精品视频免费| 成人免费看黄20分钟| 亚洲精品无码午夜福利中文字幕|