Junky's IT Notebook

          統(tǒng)計(jì)

          留言簿(8)

          積分與排名

          WebSphere Studio

          閱讀排行榜

          評(píng)論排行榜

          Drools- 商務(wù)邏輯框架的選擇

          作者:保羅.布朗

          譯者:greenbright





          版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
          作者:保羅.布朗;greenbright
          原文地址:http://www.onjava.com/pub/a/onjava/2005/08/03/drools.html
          中文地址:http://www.matrix.org.cn/resource/article/44/44046_Drools+Framework+Business.html
          關(guān)鍵詞: Drools Framework Business


          大多數(shù)網(wǎng)絡(luò)及企業(yè)級(jí)Jave應(yīng)用可以分為三部分:和用戶交互的前端,和后端系統(tǒng)(比如數(shù)據(jù)庫)交互的服務(wù)層和這兩部分之間的商務(wù)邏輯層。通常使用框架(像Struts, Cocoon, Spring, Hibernate, JDO, 和實(shí)體Beans)可以實(shí)現(xiàn)前端和后端的功能,但對(duì)于商務(wù)邏輯層卻沒有一個(gè)標(biāo)準(zhǔn)的構(gòu)建方法。像EJB和Spring只能在高端實(shí)現(xiàn)商務(wù)邏輯構(gòu)建,但卻不能組織代碼。如果我們使用在配置性,可讀性和重用性方面帶我們極大利益的框架代替那些紛繁復(fù)雜的if...then語句,那不是很好嗎?在其他領(lǐng)域我們已經(jīng)驗(yàn)證了這種利益。這篇文章建議使用Drools規(guī)則引擎作為框架來解決問題。

          下面的代碼是我們?cè)噲D避免的問題的例子。說明一個(gè)典型Java應(yīng)用中的一些商務(wù)邏輯。
          if ((user.isMemberOf(AdministratorGroup)
                && user.isMemberOf(teleworkerGroup))
               || user.isSuperUser(){      
                   // 更多對(duì)特殊案例的檢查
                   if((expenseRequest.code().equals("B203")
                     ||(expenseRequest.code().equals("A903")
                                  &&(totalExpenses<200)
                          &&(bossSignOff> totalExpenses))
                     &&(deptBudget.notExceeded)) {
                         //付款
                     } else if {
                         //檢查許多其他的條件
                     }
          } else {
               //更多商務(wù)邏輯
          }


          我們都曾經(jīng)歷過相類似或者更為復(fù)雜的商務(wù)邏輯。當(dāng)試圖在Java中采用標(biāo)準(zhǔn)方法來實(shí)施商務(wù)邏輯時(shí),就有很多問題隨之而來。
          ·        如果商務(wù)人員提出需要在本已很難理解的代碼中加入另外一個(gè)表單("C987")怎么辦?一旦所有的最初開發(fā)人員都在開發(fā)中,你愿意成為一個(gè)維護(hù)人員嗎?
          ·        如何確認(rèn)這些規(guī)則的正確與否?對(duì)技術(shù)人員來說(從來不關(guān)注商務(wù)人員),檢查規(guī)則的正確性是非常困難的。那么,有什么方法論來測(cè)試商務(wù)邏輯那?
          許多應(yīng)用有著類似的商務(wù)邏輯。如果其中一個(gè)規(guī)則發(fā)生了變化,我們能夠確認(rèn)所有系統(tǒng)中的相關(guān)部分都要改變嗎?如果一個(gè)新應(yīng)用使用了一些規(guī)則,但是也加入了一些新規(guī)則,我們需要徹底重寫邏輯嗎?
          ·        商務(wù)邏輯嵌入在Java代碼中,每次哪怕是很小的改變都需要再編譯/再部署這些代碼,這些商務(wù)邏輯容易配置嗎?
          ·        如果其他的(腳本)語言想平衡現(xiàn)有投資在商務(wù)規(guī)則邏輯中怎么辦?

          J2EE/EJB和倒置控制的框架(像Spring, Pico和Avalon)讓我們有能力在高端組織代碼。他們很提供很好的重用性,配置性和安全性,但卻不能替代上面例子中的“意大利面條式代碼”。理想地,無論我們選擇那個(gè)框架,不僅要和J2EE應(yīng)用,而且要和通常Java編程(J2SE—標(biāo)準(zhǔn)版本)及廣泛使用的表現(xiàn)和持續(xù)性框架相一致。這種框架讓我們能夠做到:
          ·        商務(wù)用戶很容易的讀和驗(yàn)證商務(wù)邏輯。
          ·        在應(yīng)用中,商務(wù)規(guī)則是可重用的和可配置的。
          ·        在重負(fù)荷下,框架是可擴(kuò)展的和性能良好的。
          ·        Java編程人員對(duì)已存在的前端(Struts, Spring)和后端框架(對(duì)象關(guān)系映射)很容易使用這種框架。

          另一個(gè)問題是在不同的應(yīng)用中,組織頁面,數(shù)據(jù)庫訪問和商務(wù)邏輯的方法也是多種多樣的。我們的框架能夠處理這個(gè)問題,并能始終提升代碼的重用性。理想地,我們的應(yīng)用使用“適用所有方式的框架”。以這種方式使用框架,能夠讓我們?cè)S多的應(yīng)用構(gòu)建的更好,讓我們可以僅關(guān)注那些對(duì)用戶更有價(jià)值的部分。

          規(guī)則引擎的營救

          如何解決這個(gè)問題那? 一個(gè)解決方案是使用規(guī)則引擎。規(guī)則引擎是組織商務(wù)邏輯的框架。它讓開發(fā)者集中精力在他們有把握的事情上,而不是在一些低級(jí)機(jī)制上作決定。
          通常,商務(wù)用戶對(duì)那些能讓他們理解是正確的事情感到更加舒服,相對(duì)于那些諸如用if...then 形式來表達(dá)的事情。你從商務(wù)專家那里聽到的一些事情如下
          ·        “10A表單用于申請(qǐng)超過200歐元的花費(fèi).”
          ·        “我們僅對(duì)數(shù)量1萬或超過1萬的交易提供分成.”
          ·        “超過10m英鎊的采購需要公司總監(jiān)的批準(zhǔn).”

          通過關(guān)注于商務(wù)用戶知道是正確的事情上,而不是怎樣用Jave代碼來表達(dá)它,上面的說明比以前我們的代碼例子要清楚的多。盡管他們已經(jīng)很清楚了,我們?nèi)匀恍枰环N機(jī)制,將這些規(guī)則應(yīng)用到商務(wù)用戶已知和作決定的事實(shí)中去。這種機(jī)制就是規(guī)則引擎。

          相關(guān)文章:
          在企業(yè)級(jí)Java應(yīng)用中使用Drools
          企業(yè)級(jí)Java應(yīng)用開發(fā)者在表現(xiàn)和持續(xù)層有很多好的框架可供選擇。但對(duì)于處在中間層的商務(wù)邏輯有好的框架嗎?你希望每次經(jīng)理給你一個(gè)新的命令就不得不重編譯那些復(fù)雜的if ... then 意大利面條代碼嗎?這篇文章中,保羅布朗推薦的Drools的規(guī)則引擎或許是完成這類任務(wù)的最好選擇。

          Jave規(guī)則引擎

          JSR 94- javax.rules API制定了與規(guī)則引擎交互的通用標(biāo)準(zhǔn),就像JDBC能夠與各種類型的數(shù)據(jù)庫交互。JSR-94并不是詳細(xì)說明實(shí)際規(guī)則到底是怎么寫的,而是最大限度的使用Java規(guī)則引擎來做這種詳細(xì)說明。
          ·        Jess可能是最成熟的Java規(guī)則引擎,有很多好的工具(包括Eclipse)和文檔。然而它是一個(gè)商業(yè)產(chǎn)品,并且它的規(guī)則是以序言樣式符號(hào)寫的,這對(duì)許多Java編程者都是一個(gè)挑戰(zhàn)。
          ·        Jena是開源框架,始于HP.它有規(guī)則引擎,并且對(duì)那些關(guān)注于語義的網(wǎng)頁特別支持。但它并不完全遵守JSR-94。
          ·        Drools是完全遵守JSR-94的規(guī)則引擎。而且是Apache模式許可下的完全開源框架。它不僅用大家熟知的Java 和XML語法來表示規(guī)則,而且有很強(qiáng)的用戶和開發(fā)者團(tuán)體。本文章中的例子中,我們將使用Drools。Drools使用像Java的語法,并且有最開放的許可。

          用Drools開始開發(fā)Java應(yīng)用

          想象一下這種情景:就在讀了這篇文章幾分鐘后,你的老板讓你給股票交易應(yīng)用建原型。盡管商務(wù)用戶還沒有定義好商務(wù)邏輯,一個(gè)可以想到的好主意就是用規(guī)則引擎來實(shí)現(xiàn)。最終的系統(tǒng)將通過網(wǎng)絡(luò)訪問,并且需要和后端數(shù)據(jù)庫和信息系統(tǒng)交流。為了開始使用這個(gè)框架,需要下載Drools框架(有依賴性)。如圖1所示,在你喜歡的IDE中建一個(gè)新項(xiàng)目并確保所有的.jars文件都被引用了。

          image
          圖1。運(yùn)行Drools必需的類庫

          由于潛在可能的巨大損失,如果我們的股票交易系統(tǒng)想要成功,在系統(tǒng)中循序漸進(jìn)的使用一系列模擬器就顯得很重要。這樣的模擬器能夠讓你確信,即使規(guī)則改變了,由系統(tǒng)的作的決定也是正確的。我們將借用靈活工具箱的一些工具,并用JUnit框架作為模擬器。

          如下例所示,我們所寫的第一段代碼是Junit測(cè)試/模擬器。盡管我們不能測(cè)試所有輸入系統(tǒng)的值的組合,由測(cè)試總比什么都不測(cè)好。在這個(gè)例子中,所有的文檔和classes(包括單元測(cè)試)又在一個(gè)文件夾/包中;但事實(shí)上,由應(yīng)該創(chuàng)建合適的包和文件夾結(jié)構(gòu)。在代碼中,我們用Log4j代替System.out調(diào)用。

          清單1:
          import junit.framework.TestCase;
          /*
          * 應(yīng)用中商務(wù)規(guī)則的JUnit測(cè)試
                  * 這也扮演商務(wù)規(guī)則的“模擬器“-讓我們說明輸入,檢驗(yàn)輸出;并在代碼發(fā)*布前看看是否達(dá)到我的期望值。
          */
          public class BusinessRuleTest extends TestCase {
          /**
          *股票購買測(cè)試
          */
            public void testStockBuy() throws Exception{
                          
          //用模擬值創(chuàng)建股票
              StockOffer testOffer = new StockOffer();
              testOffer.setStockName("MEGACORP");
              testOffer.setStockPrice(22);
              testOffer.setStockQuantity(1000);
                          
          //運(yùn)行規(guī)則
              BusinessLayer.evaluateStockPurchase(testOffer);
                          
                  //達(dá)到我們的期望嗎?
              assertTrue(
                testOffer.getRecommendPurchase()!=null);
              
              assertTrue("YES".equals(
                testOffer.getRecommendPurchase()));              
             }
          }


          這是個(gè)基本Junit測(cè)試,我們知道這個(gè)簡(jiǎn)單系統(tǒng)應(yīng)該買進(jìn)所有價(jià)格低于100歐元的股票。很顯然,沒有數(shù)據(jù)類(StockOffer.java)和商務(wù)層類(BusinessLayer.java)這個(gè)測(cè)試用例是不能編譯成功的。

          清單2:
          /**
          *示例商務(wù)邏輯的正面
          *這個(gè)簡(jiǎn)單示例里,所有的商務(wù)邏輯都包含在一個(gè)類中。
          *但在現(xiàn)實(shí)中,按需要代理給其他的類。
          */
          public class BusinessLayer {

                  /**
             *評(píng)價(jià)購買這支股票是否是個(gè)好主意
             *@參數(shù) stockToBuy
             *@return 如果推薦購買股票返回真,否則返回假
             */
            public static void evaluateStockPurchase
              (StockOffer stockToBuy){
                          return false;
            }
          }
          StockOffer類如下所示:
          /**
          * 簡(jiǎn)單的JavaBean保存StockOffer值。
          * 一個(gè)’股票出價(jià)’就是別人賣出股票(公司股份)所給出的價(jià)格。
          */
          public class StockOffer {
                  
            //常量
            public final static String YES="YES";
            public final static String NO="NO";

            //內(nèi)部變量
            private String stockName =null;
            private int stockPrice=0;
            private int stockQuantity=0;
            private String recommendPurchase = null;

          /**
             * @返回股票名稱
             */

            public String getStockName() {
                  return stockName;
            }
          /**
             * @參數(shù) stockName 設(shè)置股票名稱.
             */
            public void setStockName(String stockName) {
                  this.stockName = stockName;
            }
          /**
             * @return 返回股票價(jià)格.
             */

            public int getStockPrice() {
                  return stockPrice;
            }
          /**
             * @參數(shù) stockPrice設(shè)置股票價(jià)格.
             */

            public void setStockPrice(int stockPrice) {
                  this.stockPrice = stockPrice;
            }
          /**
             * @return 返回股票數(shù)量.
             */

            public int getStockQuantity() {
                  return stockQuantity;
            }
          /**
             * @參數(shù) stockQuantity 設(shè)置股票數(shù)量.
             */

            public void setStockQuantity(int stockQuantity){
                  this.stockQuantity = stockQuantity;
            }
          /**
             * @return 返回建議購買.
             */

            public String getRecommendPurchase() {
                  return recommendPurchase;
            }
          }


          在我們熟悉的IDE的Junit中運(yùn)行BusinessRuleTest。如果你不太熟悉Junit,可以從Junit網(wǎng)站獲取更多信息。如圖2所示,毫不奇怪的是,由于沒有準(zhǔn)備好適當(dāng)?shù)纳虅?wù)邏輯,測(cè)試在第二個(gè)申明處失敗了。這個(gè)確信了模擬器/單元測(cè)試重點(diǎn)加強(qiáng)了他們應(yīng)該有的問題。

          image
          圖2。Junit測(cè)試結(jié)果

          用規(guī)則描述商務(wù)邏輯

          現(xiàn)在,我們需要描述一些商務(wù)邏輯,像“如果股票價(jià)格低于100歐元,應(yīng)該購買。”
          這樣我們將修改BusinessLayer.java為:

          清單3:
          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;
          /**
          *示例商務(wù)邏輯的正面
          *這個(gè)簡(jiǎn)單示例里,所有的商務(wù)邏輯都包含在一個(gè)類中。
          *但在現(xiàn)實(shí)中,按需要代理給其他的類。
          *@作者 缺省
          */
          public class BusinessLayer {
          //包含規(guī)則文件的名字
            private static final String BUSINESS_RULE_FILE=
                                        "BusinessRules.drl";
                  //內(nèi)部處理的規(guī)則基礎(chǔ)
            private static RuleBase businessRules = null;
          /**
          * 如果還沒有裝載商務(wù)規(guī)則的話就裝載它。
          *@拋出異常 -通常從這里恢復(fù)
          */
            private static void loadRules()
                                 throws Exception{
              if (businessRules==null){
                businessRules = RuleBaseLoader.loadFromUrl(
                    BusinessLayer.class.getResource(
                    BUSINESS_RULE_FILE ) );
              }
            }    
                  
          /**
             *評(píng)價(jià)是否購買這支股票
             *@參數(shù) stockToBuy
             *@return 如果推薦購買股票返回真,否則返回假
             *@拋出異常
             */
            public static void evaluateStockPurchase
                 (StockOffer stockToBuy) throws Exception{
                          
          //確保商務(wù)規(guī)則被裝載
              loadRules();
          //一些程序進(jìn)行的日志
              System.out.println( "FIRE RULES" );
              System.out.println( "----------" );
                  //了解以前運(yùn)行的狀態(tài)
              WorkingMemory workingMemory
                      = businessRules.newWorkingMemory();
          //小規(guī)則集可以添加調(diào)試偵聽器
              workingMemory.addEventListener(
                new DebugWorkingMemoryEventListener());
                  //讓規(guī)則引擎了解實(shí)情
              workingMemory.assertObject(stockToBuy);
                  //讓規(guī)則引擎工作
              workingMemory.fireAllRules();
            }
          }


          這個(gè)類有許多重要的方法:
          ·        loadRules()方法裝載BusinessRules.drl文件中的規(guī)則。
          ·        更新的evaluateStockPurchase()方法評(píng)價(jià)這些商務(wù)規(guī)則。這個(gè)方法中需要注意的是:
          ·        同一個(gè)RuleSet可以被重復(fù)使用(內(nèi)存中的商務(wù)規(guī)則是無狀態(tài)的)。
          ·        由于以我們的知識(shí)我們知道什么是正確的,每次評(píng)價(jià)我們使用一個(gè)新的WorkingMemory類。在內(nèi)存中用assertObject()方法存放已知的實(shí)事(作為Java對(duì)象)。
          ·        Drools有一個(gè)事件偵聽模式,能讓我們“看到“事件模式內(nèi)到底發(fā)生了什么事情。在這里我們用它打印出調(diào)試信息。Working memory類的fireAllRules()方法讓規(guī)則被評(píng)價(jià)和更新(在這個(gè)例子中,stock offer)。

          再次運(yùn)行例子之前,我們需要?jiǎng)?chuàng)建如下BusinessRules.drl文件:

          清單4:
          <?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>
              net.firstpartners.rp.StockOffer
            </java:import>
            <!-- A Java (Utility) function we reference
              in our rules-->  
            <java:functions>
              public void printStock(
                net.firstpartners.rp.StockOffer stock)
                  {
                  System.out.println("Name:"
                    +stock.getStockName()
                    +" Price: "+stock.getStockPrice()    
                    +" BUY:"
                    +stock.getRecommendPurchase());
                  }
            </java:functions>
          <rule-set>
            <!-- Ensure stock price is not too high-->      
            <rule name="Stock Price Low Enough">
              <!-- Params to pass to business rule -->
              <parameter identifier="stockOffer">
                <class>StockOffer</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() < 100
              </java:condition>
              <!-- What happens when the business
                                rule is activated -->
              <java:consequence>
                  stockOffer.setRecommendPurchase(
                                        StockOffer.YES);  
                    printStock(stockOffer);
              </java:consequence>
            </rule>
          </rule-set>


          這個(gè)規(guī)則文件有幾個(gè)有意思的部分:
          ·        在XML-Schema定義被引入Java對(duì)象后,我們也把它引入到我們的規(guī)則中。這些對(duì)象來自于所需的Java類庫。
          ·        功能和標(biāo)準(zhǔn)的Java代碼相結(jié)合。這樣的話,我們就可以通過功能日志了解到底發(fā)生了什么。
          ·        規(guī)則設(shè)置可以包括一個(gè)或多個(gè)規(guī)則。
          ·        每條規(guī)則可以有參數(shù)(StockOffer類)。需要滿足一個(gè)或多個(gè)條件,并且當(dāng)條件滿足時(shí)就執(zhí)行相應(yīng)的結(jié)果。

          修改,編譯了代碼后,再運(yùn)行Junit測(cè)試模擬器。這次,如圖3所示,商務(wù)規(guī)則被調(diào)用了,邏輯評(píng)價(jià)正確,測(cè)試通過。祝賀—你已經(jīng)創(chuàng)建了你的第一個(gè)基于規(guī)則的應(yīng)用!

          image
          圖3。成功的Junit測(cè)試

          靈活的規(guī)則

          剛建好系統(tǒng),你示范了上面的原型給商務(wù)用戶,這時(shí)他們想起先前忘了給你提到幾個(gè)規(guī)則了。新規(guī)則之一是當(dāng)數(shù)量是負(fù)值時(shí),不能夠交易股票。你說“沒問題,”,然后回到你的座位,借可靠的知識(shí),你能迅速的改進(jìn)系統(tǒng)。
          第一件要做的事情是更新你的模擬器,把下面的代碼加到BusinessRuleTest.java:

          清單5:

          /**
          *測(cè)試買股票確保系統(tǒng)不接受負(fù)值
          */
            public void testNegativeStockBuy()
                                          throws Exception{
          //用模擬值創(chuàng)建股票
                StockOffer testOffer = new StockOffer();
                  testOffer.setStockName("MEGACORP");
                  testOffer.setStockPrice(-22);
                  testOffer.setStockQuantity(1000);
          //運(yùn)行規(guī)則
                  BusinessLayer
                        .evaluateStockPurchase(testOffer);
          //是否達(dá)到我們的期望?
                  assertTrue("NO".equals(
                    testOffer.getRecommendPurchase()));
          }


          這是為商務(wù)用戶描述的新規(guī)則的測(cè)試。如果測(cè)試這個(gè)Junit測(cè)試,如預(yù)期的新測(cè)試失敗了。我們需要加這個(gè)新規(guī)則到.drl文件中,如下所示。

          清單6:
          <!-- 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ī)則的和前一個(gè)類似,期望<java:condition>不同(測(cè)試負(fù)值)和<java:consequence>設(shè)置建議購買為No。再次運(yùn)行測(cè)試/模擬器,這次測(cè)試通過。

          這樣的話,如果你習(xí)慣使用過程編程(象大多數(shù)Java 編程者),或許你會(huì)發(fā)愁撓頭:一個(gè)文件包含兩個(gè)獨(dú)立的商務(wù)規(guī)則,而且我們也沒有告訴規(guī)則引擎這兩個(gè)規(guī)則哪個(gè)更重要。然而,股票價(jià)格(-22)也符合兩個(gè)規(guī)則(它小于0也小于100)。不論如何,我們得到了正確的結(jié)果,即使交換規(guī)則的順序。這是怎么工作的那?

          下面控制臺(tái)輸出的摘錄幫助我們明白到底發(fā)生了什么。我們看到兩個(gè)規(guī)則都被觸發(fā)了([activationfired]行)并且Recommend Buy先被置為Yes然后被置為No。Drools是如何以正確的順序來觸發(fā)這些規(guī)則的那?如果你看Stock Price Low Enough規(guī)則,就會(huì)看到其中的一個(gè)條件recommendPurchase()是null.這就足以讓Drools決定Stock Price Low Enough規(guī)則應(yīng)該先于Stock Price Not Negative規(guī)則觸發(fā)。這個(gè)過程就是沖突解決方案。

          清單7:
          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


          如果你是個(gè)過程編程者,無論你認(rèn)為這是多聰明,你仍然不能完全的信任它。這是為什么我們使用單元測(cè)試/模擬器:“辛苦的”Junit測(cè)試(使用通用Java代碼)保證規(guī)則引擎用我們關(guān)注的行來做決定。(不要花費(fèi)上億在一些無價(jià)值的股票上!)同時(shí),規(guī)則引擎的強(qiáng)大和靈活性讓我們能夠迅速開發(fā)商務(wù)邏輯。
          稍后,我們將看到更多沖突解決方案的經(jīng)典的方式。

          沖突解決方案

          在商務(wù)這邊,人們真的印象深刻并別開始通過可能的選擇來思考。他們看到XYZ公司股票的問題并且決定執(zhí)行一條新規(guī)則:如果XYZ公司的股價(jià)低于10歐元,就僅僅買XYZ公司的股票。
          象上次,加測(cè)試到模擬器中,在規(guī)則文件中加入新的商務(wù)規(guī)則,如下所列。首先,在BusinessRuleTest.java中添加新方法。

          清單8:
          /**
          *確保系統(tǒng)系統(tǒng)在XYZ公司股價(jià)便宜時(shí)就購買他們的股票
          */
          public void testXYZStockBuy() throws Exception{
          //用模擬值創(chuàng)建股票
            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);
          //運(yùn)行規(guī)則
            BusinessLayer.evaluateStockPurchase(
              testOfferLow);
            assertTrue("YES".equals(
              testOfferLow.getRecommendPurchase()));
                          
            BusinessLayer.evaluateStockPurchase(
              testOfferHigh);
            assertTrue("NO".equals(
              testOfferHigh.getRecommendPurchase()));            
          }


          然后,在BusinessRules.drl中加個(gè)新<規(guī)則>:

          清單10:
            
          <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>


          注意到商務(wù)規(guī)則文件中,規(guī)則名字后,我們?cè)O(shè)置salience為-1(比如,到現(xiàn)在我們說明的所有規(guī)則中的最低優(yōu)先級(jí))。系統(tǒng)沖突(意味著Drools在觸發(fā)那個(gè)規(guī)則的順序上作決定)的大多數(shù)規(guī)則給出了將達(dá)到的規(guī)則的條件。缺省的決定方法如下:.
          ·        顯著性:如上列出的我們分配的值。
          ·        嶄新性:我們使用規(guī)則的次數(shù)。
          ·        復(fù)雜性:第一次觸發(fā)的更復(fù)雜的特定規(guī)則。
          ·        裝載順序:那個(gè)規(guī)則被裝載的順序。
          如果我們不說明這個(gè)例子中規(guī)則的顯著性,將會(huì)發(fā)生的是:
          ·        XYZ公司規(guī)則(“如果股票價(jià)格超過10歐元買XYZ”)將被首先觸發(fā)(推薦買標(biāo)記的狀態(tài)被置為No)。
          ·        然后是更多的普通規(guī)則被觸發(fā)(“買所有低于100的股票”),推薦買標(biāo)記的狀態(tài)被置為yes。

          這將得到我們不期望的結(jié)果。然而,我們的例子設(shè)置了顯著性因素,用例及商務(wù)規(guī)則就會(huì)如我們期望的運(yùn)行了。

          大多數(shù)情況下,書寫清楚的規(guī)則且設(shè)置顯著性將為Drools提供足夠的信息來選擇觸發(fā)規(guī)則的順序。有時(shí),我們想整個(gè)的改變解決規(guī)則沖突的方式。以下是一個(gè)如何改變的例子,其中我告訴規(guī)則引擎首先觸發(fā)最簡(jiǎn)單的規(guī)則。要注意的是,改變沖突解決方案是要小心,因?yàn)樗芨镜母淖円?guī)則引擎的規(guī)則—用清楚的寫得恰當(dāng)?shù)囊?guī)則能預(yù)先解決許多問題。

          清單11:
          //生成沖突解決者的列表
            ConflictResolver[] conflictResolvers =
              new ConflictResolver[] {
                SalienceConflictResolver.getInstance(),
                RecencyConflictResolver.getInstance(),
                  SimplicityConflictResolver.getInstance(),
                  LoadOrderConflictResolver.getInstance()
              };
          //包裝成合成解決者
            CompositeConflictResolver resolver =
              new CompositeConflictResolver(
                conflictResolvers);
          //當(dāng)裝載規(guī)則時(shí),說明這個(gè)解決者
            businessRules = RuleBaseLoader.loadFromUrl(
              BusinessLayer.class.getResource(
                BUSINESS_RULE_FILE),resolver);


          對(duì)于我們的簡(jiǎn)單的應(yīng)用,由Junit測(cè)試驅(qū)動(dòng),我們不需要改變Drools解決規(guī)則沖突的方式。了解沖突解決方案是如何工作的對(duì)我們是非常有幫助的,尤其是當(dāng)應(yīng)用需要滿足更復(fù)雜根嚴(yán)格的需求試。

          結(jié)論

          這篇文章論證了許多編程者遇到過的問題:如何定制復(fù)雜的商務(wù)邏輯。我們示范了一個(gè)用Drools為解決方案的簡(jiǎn)單應(yīng)用并介紹了基于規(guī)則編程的概念,包括在運(yùn)行時(shí)這些規(guī)則是如何被處理的。稍后,接下來的文章將以這些為基礎(chǔ)來展示在企業(yè)級(jí)Java應(yīng)用中是如何使用它的。


          資源
          ·下載例子所有源代碼:源代碼
          ·Matrix-Java開發(fā)者社區(qū):http://www.matrix.org.cn
          ·onjava.com:onjava.com
          ·Drools項(xiàng)目主頁
          ·Drools規(guī)則信息
          ·“Drools和規(guī)則引擎介紹”由Drools項(xiàng)目領(lǐng)導(dǎo)
          ·Drools規(guī)則計(jì)劃文件
          ·JSR-94, Java規(guī)則引擎,概略
          ·Jess Jave規(guī)則引擎
          ·Jena語義和規(guī)則引擎
          ·JSR-94主頁
          ·Jess在Action主頁
          ·“商務(wù)規(guī)則思想”(基于Jess)
          ·“規(guī)則系統(tǒng)的一般介紹”
          ·“Rete算法的Jess執(zhí)行”

          保爾 布朗已經(jīng)通過FirstPartners.net網(wǎng)站為企業(yè)級(jí)Java咨詢了約7年的時(shí)間。

          posted on 2007-06-27 18:16 junky 閱讀(328) 評(píng)論(0)  編輯  收藏 所屬分類: 規(guī)則引擎


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 芜湖市| 西乌珠穆沁旗| 平顺县| 滨州市| 芜湖市| 柳江县| 呼图壁县| 武汉市| 卫辉市| 仪征市| 晋城| 金阳县| 安丘市| 锡林郭勒盟| 克什克腾旗| 新巴尔虎左旗| 尼勒克县| 延长县| 临猗县| 双桥区| 磐石市| 封开县| 大港区| 岳西县| 洛扎县| 隆化县| 新蔡县| 新丰县| 建水县| 泾阳县| 科尔| 和顺县| 葫芦岛市| 颍上县| 洪泽县| 高邑县| 清河县| 资中县| 惠州市| 岱山县| 镇沅|