zoukankan      html  css  js  c++  java
  • Drools业务逻辑框架

    大部分 web 以及企业级 Java 应用可被分成三部分:与用户交互的前台,与数据库这样的后台系统交互的服务层,以及它们之间的业务逻辑。最近这段时间,通常我们会使用框架来实现前台和后台的需求(例如:Struts, Cocoon, Spring, Hibernate, JDO, 以及实体 Beans),但是却没有一种标准手段很好的组织业务逻辑。像 EJB 和 Spring 这样的框架都以 high level 方式处理,这无助于组织我们的代码。除非我们改变这种凌乱,否则系统将不会健壮,框架中杂乱的 if...then 语句能带给我们可配置性、可读性的优点,以及在其他地方复用代码的愉悦吗?本文将介绍如何使用 Drools 规则引擎框架来解决这些问题。
       
        下列的范例代码展示了我们正要试图努力避免的问题。展示了包含一些业务逻辑的 Java 典型应用。

    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
    }

        我们经常写出类似的(甚至更复杂)业务逻辑。当这些用 Java 实现的业务逻辑成为标准方式时,将存在下列问题:
       
            业务用户怎样在这些难以理解的代码基础上添加另一个条件(比如"C987")?一旦最初开发这些程序的员工离开了,你想成为维护这些代码的人吗?
            我们怎样检查规则的正确性?对业务伙伴的技术人员来说都够复杂的了,更不要说检查。我们可以有系统的测试这些业务逻辑吗?
            很用应用都有相似的业务规则--当其中的一个规则改变,我们能保证这一改变可贯穿整个系统?当新应用使用这些规则,该应用已经部分添加了新的规则,但不完全,我们要把逻辑重写过吗?
            我们经常需要对每个细小调整所带来的改变进行重编译/重部署,而不是坚实的依靠 Java 代码,业务逻辑是否易于配置?
            可否复用已存在的用其他(脚本)语言编写的业务规则逻辑?
           
        J2EE/EJB 以及“IoC inversion of control”框架(比如 Spring,Pico,以及 Avalon)给我们带来的是 high level 代码组织能力。在提供良好复用性、可配置性、以及安全性的同时,没有一个能替代(解决)以上的“spaghetti 代码”范例出现的问题。理想地,无论选择何种框架,不仅仅适合 J2EE 应用,而且也可用于“normal”Java(J2SE)程序,以及大部分普遍采用的表现层以及持久层框架。这种理想框架应该允许我们这样做:
       
            业务用户应该可以方便的阅读和校验业务逻辑。
            业务规则应该可被复用,并可以通过程序进行配置。
            这种框架应该是可升级的,并在高负载情况下运行。
            Java 程序员可以像使用现有的前台(Struts,Spring)和后台(ORM object-relational mapping)框架一样方便地使用这个框架。
           
        另外的问题是,有许多的 web 页面、数据库访问组织方式,业务逻辑在这两种应用中应趋于不同。而框架应该能应付这些并促进代码复用。理想的框架将能“frameworks all the way down.”,通过这种方式使用框架,我们能在应用中大量的“out of the box”,这样我们只为客户记录添加值的部分。
       
    规则引擎前来救援

        我们怎样解决问题呢?一种方案是通过规则引擎获取 traction。规则引擎是为组织业务逻辑应运而生的框架,它让开发者专注于做被认为正确的事情上,而不是以 low-level 方式作出决定。
       
        通常,业务用户舒适的表达他们知道的正确的事,而不是 if...else 格式的表达方式。你也许能从业务专家听见这些东西:
       
        “FORM 10A 用来索取额外 200 欧元费用。(FORM 10A is used for expense claims over 200 Euro.)”
        “我们只进行数量在 10,000 以上的贸易。”
        “购买大于 €10m 的要经过公司董事批准。”
       
        通过专注于我们认为正确的事情上,而不是只知道怎样用 Java 代码表达,那么上面的叙述将比之前的代码范例更清晰。我们仍然需要一种机制为我们知道和做决定的事实应用这些规则。这种机制就是规则引擎。
       
    Java 中的规则引擎
       
        JSR 94,如同 JBDC 允许我们与多种数据库交互一样,javax.rules 是一组与规则引擎交互的通用标准 API。为什么 JSR-94 没有详细说明实际的规则怎样书写,有下面大量的 Java 规则引擎可供选择:
       
        Jess 或许是最成熟的 Java 规则引擎,有良好的工具支持(包括 Eclipse 插件)以及文档。但是,它是商业软件,而且用 Prolog-style 符号书写规则,对 Java 程序员来说是很晦涩的。
        Jena 是一套开源框架,最初由惠普发起。虽然它有规则引擎以及在 Web 语义方面特别强大,但它并不与 JSR-94 兼容。
        Drools 是与 JSR-94 兼容的规则引擎,并且在 Apache-style 许可下完全开源。它不仅用熟悉的 Java 和 XML 语法表述规则,而且它还有强大的用户、开发者社区。在本文中有范例,我们将使用 Drools,因为它有最容易使用的类似 Java 的语法以及完全开发许可。
       
    利用 Drools 开始 Java 开发

        假设有这样的场景:在阅读本文的数分钟后,你老板要求你做一个股票交易应用原型。这时,业务用户尚未被完全定义业务逻辑,你马上会想到最好的办法是用规则引擎实现。最终系统将可通过内部网访问,而且还要和后台数据库以及消息系统通讯。在着手行动前,先下载 Drools 框架(与支持库一起)。在你喜欢的 IDE 中创建新项目,确定所有 .jar 文件被引用进项目,如图 1 中所示。截图是基于 Eclipse 的,不过在其他 IDE 中创建也是相似的。

          Libraries needed to Run Drools
                       图 1. 运行 Drools 所需要的库
       
        如果我们的股票交易系统很混乱,将失去大量潜在客户(商机),所以在系统的整个步骤中放入一些模拟器(simulator)是至关重要的。这种模拟器给了你决心采用该系统的信心,甚至规则改变以后所带来的麻烦。我们将借助敏捷工具箱中的工具,以及 JUnit(http://www.junit.org/) 框架进行模拟。
       
        如下,我们写的第一段代码是 JUnit 测试/模拟器。即使我们无法测试每个对应用有价值的输入组合,但有测试也比没有测试的好。在这个范例中,所有的文件和类(包括单元测试)都放入一个文件夹/包中,但实际上,你可能会用一种适当的包、文件夹结构。范例代码中我们用 Log4j 代替 System.out 调用。

    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 测试,我们知道我们的系统应该买所有低于 100 欧元的股票。很明显,要是没有数据持有类(StockOffer.java)和业务层类(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 网站找到更多信息。不必惊讶,如图 2 所示第二个断言测试失败,这是因为还没把业务逻辑放在适当的地方。测试结果用高亮显示了模拟器/单元测试所出现的问题,这是很保险的。

      JUnit Test Results
                  图 2. JUnit 测试结果
                 
    用规则编写业务逻辑
       
        在这里,我们要写一些业务逻辑,来表达“一旦股票价格低于 100 欧元,就马上购买。” 要达到这个目的,需调整 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 文件加载规则。
        更新后的 evaluateStockPurchase(),用于评估业务规则。这个方法的注解如下:
            可以反复复用相同的 RuleSet(内存中的业务规则是无状态的)。
            为每次评估构造新的 WorkingMemory,因为我们的知识知道这个时刻是正确的。使用 assertObject() 放置已知事实(作为 Java 对象)到内存中。
            Drools 有个事件监听模式,允许我们“查看”事件模型中到底发生了什么。在这里我们用它打印 debug 信息。
            working memory 类中的 fireAllRules() 方法评估和更新规则(在本例中是股票出价)。
       
        再次运行该范例前,需要创建我们的 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>

        该规则文件有些有趣部分:
       
            只有在 XML-Schema 定义 Java 对象之后,我们才能引用进规则。这些对象可以是来自于任何必须的 Java 类库。
            接下来是 functions,它们可以与标准 Java 代码进行混合。既然这样,我们干脆混入些日志功能来帮助我们观察发生了什么。
            再下来是我们的 rule set,rule set 由一到多个规则组成。
            每个规则可持有参数(StockOffer 类),并需要实现一个或多个条件,当条件符合时,将会执行相应结果。
           
        在修改和编译完代码后,再次运行 JUnit 测试。这次调用了业务规则,我们的逻辑进行正确地评估,并且测试通过,参看图 3。恭喜--你已经构建了第一个基于规则的应用!

        Successful JUnit Test
        图 3.成功的 JUnit 测试
       
    使规则更聪明

        刚刚构建好应用,你就向业务用户示范上面的原型,他们却忽然想起先前并没有提出的规则。其中一个新规则是当数量是负数时(<0)不能进行股票交易。“没关系,”你说,接着回到办公桌上,紧扣已有知识,快速演化你的系统。
       
        首先要更新模拟器,把以下代码添加到 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()));
    }

        这个测试是为业务用户描述的新规则建立的。正如意料之中的,如果运行 JUnit 测试,我们的新测试将失败。所以,我们要添加新的规则到 .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>

        这个规则的格式和前面的相似,除了 (用于测试负数)以及 用于设置推荐购买为 No 以外。我们再次运行测试,这次通过了。
       
        这时,如果你习惯于过程化编程(像大多数 Java 程序员一样),你也许要搔头皮了:在一个文件中包含两个独立的业务规则,而且我们也没告诉规则引擎哪个更重要。不管怎样,股票价格(对于 -22)都满足两个规则(也就是少于 0 和少于 100)。尽管这样,我们仍能得到正确结果,即使交换规则顺序。这是怎么做到的呢?
       
        下面的控制台输出有助于我们了解到底怎么回事。我们看见两个规则都执行了([activationfired] 这行),Recommend Buy 第一次被设置为 Yes 接着又被设置成 No。Drools 怎么知道执行这些规则的正确顺序呢?如果你观察 Stock Price Low Enough 规则,将发现 recommendPurchase() 其中一个条件为空。通过这点,Drools 规则引擎足以判断 Stock Price Low Enough 规则应该在 Stock Price Not Negative 规则之前执行。这个过程称为 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


        如果你是一名过程化程序员,无论你用怎样聪明的方式考虑这些,你都不会完全相信。这就是为什么要进行单元/模拟器测试的原因:进行 "坚固的" JUnit 测试(使用一般 Java 代码)确保规则引擎所作出的决定是按照我们所想要的路线进行。(不会花费大量金钱在无价值的股票上)同时,规则引擎的强大和伸缩性允许我们快速开发业务逻辑。
       
        稍后,我们将学习如何用更加精练的解决方案进行冲突处理。
       
    冲突结局方案

        现在业务伙伴被打动了,并且开始考虑进行选择了。随即他们遇到了个 XYZ 公司股票的问题,那么我们来实现新规则吧:只有 XYZ 公司股票低于 10 欧元才可购买。
       
        像以前一样,添加测试到模拟器,接着在规则文件中包含新业务规则。首先在 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 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>

        注意业务规则文件,在 rule name 后面,我们把 salience 设置成 -1(到目前为止了解的最低优先级)。大多数规则在系统中是冲突的,这意味着 Drools 必须为规则的执行顺序做判断,假设这些条件都与规则匹配。默认的判断方式是:
           
        Salience:赋予的值。
        Recency:使用规则的次数。
        Complexity:首先执行有复杂值的特定规则。
        LoadOrder:规则载入的顺序。
       
        如果没有显示的在规则中详细指明,将会发生:
       
        XYZ 公司规则("当价格高于 10 欧元就不购买 XYZ 的股票")将先执行(Recommend Buy 标志被设置为 No)。
        接着更多的一般规则("购买所有 100 欧元以下的股票")被执行,把 Recommend Buy 标志设置为 yes。
       
        这会给我们一个不想要的结果。然而,一旦在范例中设置了 saliency 要素,最终的测试和业务规则将像预期的那样顺利运行。
       
        大多数时间,编写清晰的规则和设置 saliency 将给 Drools 足够信息以选择合适的顺序执行规则,有时我们想改变整个规则冲突处理方式。下面的例子说明了如何改变,告诉规则引擎首先执行最简单的规则。要注意的是:改变冲突解决方案要小心,它可能从根本上改变规则引擎的行为。

      //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);

        我们的简单应用由 JUnit 测试驱动,我们不必改变 Drools 处理规则冲突的方式。知道冲突解决方案怎样运作是很有用的,尤其当你的应用为了迎合更复杂、更苛刻的需求时。
       
    结束
       
        本文示范了大部分程序员不得不面对的问题:怎样安排复杂业务逻辑的顺序。我们示范了一个使用 Drools 作为解决方案并引入基于规则编程概念的简单应用,包括了怎样在运行时处理规则。接着,后续文章使用这些技术并展示了怎样在企业级 Java 应用中使用。

  • 相关阅读:
    donet core 2.1 DateTime ToString() 方法 在不同平台返回的时间格式不一样?
    asp.net core 2.1 post 无法提交参数?
    重写$.ajax方法
    基于git 客户端使用shell工具
    NPOI 自定义单元格背景颜色-Excel
    Ubuntu 1604配置安装mysql8.0
    Fiddler拦截并修改移动端请求
    MFC路径层的使用(BeginPath和EndPath函数)
    MFC中设备描述表dc的使用
    不能从const char *转换为LPCWSTR --VS经常碰到
  • 原文地址:https://www.cnblogs.com/muyuge/p/6152664.html
Copyright © 2011-2022 走看看