kapok

          垃圾桶,嘿嘿,我藏的這么深你們還能找到啊,真牛!

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            455 隨筆 :: 0 文章 :: 76 評論 :: 0 Trackbacks
          <2005年9月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          常用鏈接

          留言簿(28)

          隨筆分類

          隨筆檔案

          Corba

          EAI

          Online Document

          Open Source

          Portal

          RSS Links

          Weblogic

          不錯的鏈接

          工具集合

          數(shù)據(jù)倉庫相關(guān)

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          http://rosen.blogjava.net/archive/2005/09/11/12668.html

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

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

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

                Libraries needed to Run Drools
                             圖 1. 運行 Drools 所需要的庫
             
              如果我們的股票交易系統(tǒng)很混亂,將失去大量潛在客戶(商機(jī)),所以在系統(tǒng)的整個步驟中放入一些模擬器(simulator)是至關(guān)重要的。這種模擬器給了你決心采用該系統(tǒng)的信心,甚至規(guī)則改變以后所帶來的麻煩。我們將借助敏捷工具箱中的工具,以及 JUnit(http://www.junit.org/) 框架進(jìn)行模擬。
             
              如下,我們寫的第一段代碼是 JUnit 測試/模擬器。即使我們無法測試每個對應(yīng)用有價值的輸入組合,但有測試也比沒有測試的好。在這個范例中,所有的文件和類(包括單元測試)都放入一個文件夾/包中,但實際上,你可能會用一種適當(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 測試,我們知道我們的系統(tǒng)應(yīng)該買所有低于 100 歐元的股票。很明顯,要是沒有數(shù)據(jù)持有類(StockOffer.java)和業(yè)務(wù)層類(BusinessLayer.java)它將無法編譯。這兩個類如下。
             
          /**
           * 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;
            }
          }

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

            JUnit Test Results
                        圖 2. JUnit 測試結(jié)果
                       
          用規(guī)則編寫業(yè)務(wù)邏輯
             
              在這里,我們要寫一些業(yè)務(wù)邏輯,來表達(dá)“一旦股票價格低于 100 歐元,就馬上購買。” 要達(dá)到這個目的,需調(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();
            }
          }

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

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

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

              剛剛構(gòu)建好應(yīng)用,你就向業(yè)務(wù)用戶示范上面的原型,他們卻忽然想起先前并沒有提出的規(guī)則。其中一個新規(guī)則是當(dāng)數(shù)量是負(fù)數(shù)時(<0)不能進(jìn)行股票交易。“沒關(guān)系,”你說,接著回到辦公桌上,緊扣已有知識,快速演化你的系統(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()));
          }

              這個測試是為業(yè)務(wù)用戶描述的新規(guī)則建立的。正如意料之中的,如果運行 JUnit 測試,我們的新測試將失敗。所以,我們要添加新的規(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>

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

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

              現(xiàn)在業(yè)務(wù)伙伴被打動了,并且開始考慮進(jìn)行選擇了。隨即他們遇到了個 XYZ 公司股票的問題,那么我們來實現(xiàn)新規(guī)則吧:只有 XYZ 公司股票低于 10 歐元才可購買。
             
              像以前一樣,添加測試到模擬器,接著在規(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()));            
          }

              接下來向 BusinessRules.drl 中添加新 <rule>:
             
            <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)先級)。大多數(shù)規(guī)則在系統(tǒng)中是沖突的,這意味著 Drools 必須為規(guī)則的執(zhí)行順序做判斷,假設(shè)這些條件都與規(guī)則匹配。默認(rèn)的判斷方式是:
                 
              Salience:賦予的值。
              Recency:使用規(guī)則的次數(shù)。
              Complexity:首先執(zhí)行有復(fù)雜值的特定規(guī)則。
              LoadOrder:規(guī)則載入的順序。
             
              如果沒有顯示的在規(guī)則中詳細(xì)指明,將會發(fā)生:
             
              XYZ 公司規(guī)則("當(dāng)價格高于 10 歐元就不購買 XYZ 的股票")將先執(zhí)行(Recommend Buy 標(biāo)志被設(shè)置為 No)。
              接著更多的一般規(guī)則("購買所有 100 歐元以下的股票")被執(zhí)行,把 Recommend Buy 標(biāo)志設(shè)置為 yes。
             
              這會給我們一個不想要的結(jié)果。然而,一旦在范例中設(shè)置了 saliency 要素,最終的測試和業(yè)務(wù)規(guī)則將像預(yù)期的那樣順利運行。
             
              大多數(shù)時間,編寫清晰的規(guī)則和設(shè)置 saliency 將給 Drools 足夠信息以選擇合適的順序執(zhí)行規(guī)則,有時我們想改變整個規(guī)則沖突處理方式。下面的例子說明了如何改變,告訴規(guī)則引擎首先執(zhí)行最簡單的規(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);

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


          請注意!引用、轉(zhuǎn)貼本文應(yīng)注明原譯者:Rosen Jiang 以及出處:http://www.aygfsteel.com/rosen

          posted on 2005-09-12 22:11 笨笨 閱讀(2513) 評論(0)  編輯  收藏 所屬分類: J2EEALL
          主站蜘蛛池模板: 虹口区| 称多县| 武鸣县| 阳春市| 焦作市| 化德县| 蓝田县| 平原县| 北海市| 沙雅县| 杭锦旗| 曲沃县| 英山县| 广水市| 漳平市| 彭泽县| 依兰县| 鸡泽县| 和硕县| 乐东| 仁怀市| 罗山县| 虎林市| 贵南县| 灵台县| 会昌县| 楚雄市| 德清县| 洪雅县| 壶关县| 云阳县| 榆树市| 双桥区| 玉门市| 义乌市| 漳浦县| 西华县| 新郑市| 鹤山市| 雅江县| 修武县|