• drools 学习笔记


    2009-05-06 06:24

    大多数web和企业Java应用可以分成三个部分:一个和用户交互的前台, 一个和后台系统,例如数据库交互的服务层,以及他们中间的业务逻辑。 现在使用框架构建前台和后台系统已经成为普遍共识(例如, Struts, Cocoon, Spring, Hibernate, JDO, 和 Entity Beans), 但却没有一个标准的方法来构建业务逻辑。一些框架,例如 EJB 和 Spring 只在一个高层实现业务逻辑,但对于我们组织逻辑代码没有任何帮助,所以,为什么没有一个框架来替换冗繁,易错的if...then语句呢,这个框架应该和其它前台或后台框架一样,易于配置,具有可读性和重用性。下面我们将介绍Drools 规则引擎,这个来解决我们问题的框架。 
    我们经常能见到噩梦般的业务逻辑代码: 
    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 

    当然会有一些优秀的程序员可以写出更漂亮些的代码,但那是相当费神的。而Drools等规则引擎 提供了一种很好的解决途径。 
    先来看Drools的一个简单的例子: 
       应用:某公司要对某一次参与生产的员工计算工资。 
       工资=产量*基本工资*系数 
       系数与该员工的产品合格率有关: 合格率=1 系数=1;0.9<=合格率<1 系数=0.95 ;0.85<=合格率<9 系数=0.9;0.8<=合格率<0.85 系数=0.8;合格率<0.8 系数=0.6; 
       假设我们有这么一个类:

    Java代码
    1.   public class Employee {   
    2. private String name;   
    3.   
    4. private int product;   
    5.   
    6. private float hgl;   
    7. ...   
    8.    }  
    [java] view plaincopy
     
    1. public class Employee {  
    2.  private String name;  
    3.   
    4.  private int product;  
    5.   
    6.  private float hgl;  
    7.  ...  
    8.    }  


       上面应用用Drools的drl 应该这样描述:  

    Java代码
    1. rule "FirstGz"  
    2.        
    3.      when   
    4.          e:Employee(hgl==1);   
    5.      then   
    6.          System.out.println(e.getName()+"的工资:"+e.getProduct()*50*1);   
    7.            
    8. end   
    9.   
    10. rule "SecondGz"  
    11.      when   
    12.          e:Employee(hgl<1,hgl>=0.9);   
    13.      then   
    14.          System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.95);   
    15.          retract(e);   
    16.            
    17. end   
    18.   
    19. rule "3Gz"  
    20.      when   
    21.          e:Employee(hgl<0.9,hgl>=0.85);   
    22.      then   
    23.          System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.9);   
    24.          retract(e);   
    25.            
    26. end   
    27.   
    28. rule "4Gz"  
    29.      when   
    30.          e:Employee(hgl<0.85,hgl>=0.8);   
    31.      then   
    32.          System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.85);   
    33.          retract(e);   
    34.            
    35. end   
    36.   
    37. rule "5Gz"  
    38.      when   
    39.          e:Employee(hgl<0.8);   
    40.      then   
    41.          System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.6);   
    42.          retract(e);   
    43.            
    44. end   
    [java] view plaincopy
     
    1. rule "FirstGz"  
    2.    
    3.  when  
    4.   e:Employee(hgl==1);  
    5.  then   
    6.      System.out.println(e.getName()+"的工资:"+e.getProduct()*50*1);  
    7.     
    8. end  
    9.   
    10. rule "SecondGz"  
    11.  when  
    12.   e:Employee(hgl<1,hgl>=0.9);  
    13.  then   
    14.      System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.95);  
    15.      retract(e);  
    16.     
    17. end  
    18.   
    19. rule "3Gz"  
    20.  when  
    21.   e:Employee(hgl<0.9,hgl>=0.85);  
    22.  then   
    23.      System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.9);  
    24.      retract(e);  
    25.     
    26. end  
    27.   
    28. rule "4Gz"  
    29.  when  
    30.   e:Employee(hgl<0.85,hgl>=0.8);  
    31.  then   
    32.      System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.85);  
    33.      retract(e);  
    34.     
    35. end  
    36.   
    37. rule "5Gz"  
    38.  when  
    39.   e:Employee(hgl<0.8);  
    40.  then   
    41.      System.out.println(e.getName()+"的工资:"+e.getProduct()*50*0.6);  
    42.      retract(e);  
    43.     
    44. end  


       
    这样就把业务逻辑和程序分开来了。如果要修改业务逻辑,那也是很容易的,只要修改Rule文件就可以了,而Rule文件是可以在一种类似XML的独立文件,可以很方便的替换修改。

    Drools 是一个开源的规则引擎,目前有Jboss管理,最新版本为5.X。 
    我们使用Drools就是为了让它处理数据与规则的关系,因此Drools要获得数据和获得规则,然后进行执行。因此Drools分为编制和运行时两个部分。 
    编制是指产生rule的过程,Drools用DRL,或者XML来描述规则。 
    编制的过程包括为规则建立DRL 或XML 文件,传入一个由Antlr 3 文法器定义的解析器中。解析器对文件中规则文法的正确性进行检查并为descr 建立一个中间结构,在AST 中的descr 代表规则的描述。AST 然后将descr 传入Package Builder中,由其进行打包。Package Builder 同时负责包括打包中用到的所有代码产生器和编译器。Package 对象是自包含并可配置的,它是一个包含规则的序列化的对象。 


    RuleBase 是运行时组件,包含一个或多个Package。Package 在任何时候都可以向RuleBase中添加或删除。一个RuleBase 可以同时初始化多个Working Memory,在其间维护着一个弱引用,除非重新进行配置。Working Memory 包含许多子组件,如Working Memory Event Support(事件支持),Truth Maintenance System(真值维护系统), Agenda 和 Agenda Event Support(事件支持)。向Working Memory 中设置对象的工作可能要在建立了一个或多个激活的规则后才结束。Agenda 负有规划激活规则运行的责任。 


    以上是Drools的总体架构,其主要有以下类实现: 
    编制: 
    XmlParser,DrlParser 分别用来解析XML描述的规则文件和DRL描述的规则文件。 
    PackageBuilder 创建package实例。 
    例如: 
    PackageBuilder builder = new PackageBuilder(); 
    builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "package1.drl" ) ) ); 
    builder.addPackageFromXml( new 
    InputStreamReader( getClass().getResourceAsStream( "package2.xml" ) ) ); 

    Package pkg = builder.getPackage(); 

    运行时的类: 
    RuleBase 使用RuleBaseFactory 实例化,默认情况下返回一个ReteOO 的RuleBase。Package通过使用addPackage 方法按顺序加入。你可以指定任何名称空间的Packages 或者同一名称的多个包加入RuleBase。 

    RuleBase ruleBase = RuleBaseFactory.newRuleBase(); 
    ruleBase.addPackage( pkg ); 

    事实数据相关类: 
    WorkingMemory 保存运行时事实数据的地方。 
    由ruleBase产生:WorkingMemory wm= ruleBase.newStatefulSession(); 
    加载事实数据: 
    wm.insert(object ); 

    insert方法返回一个FactHandle对象指向workingMemory中对象的引用。如果要对Object进行修改删除等操作都要通过FactHander对象来完成。 

    在准备好Rule,和Fact后 就可以调用 WorkingMemory对象的 fireAllRules()方法执行规则引擎。 

    Agenda上面提到过它负有规划激活规则运行的责任。 
    它运行过程分两个阶段: 
    1) WorkingMemory Actions : assert 新的 facts ,修改存在的 facts 和 retract facts 
    都是 WorkingMemory Actions 。通过在应用程序中调用 fireAllRules() 方法,会使引擎 
    转换到 Agenda Evaluatioin 阶段。 
    2) Agenda Evaluation :尝试选择一条规则进行激发( fire )。如果规则没有找到就 
    退出,否则它就尝试激发这条规则,然后转换到 WorkingMemory Actions 阶段,直到 Agenda 
    中为空。 

    Drools提供了一些监听器来获得规则引擎执行过程中发生的一些事件: 
    WorkingMemoryEventListene,AgendEventListener和RuleFlowEventListener 

    从名称来看我们也大概能知道他们分别的作用: 
    WorkingMemoryEventListene是监听WorkingMemory中发生的一些时间,WorkingMemory发生的事件那就是Fact的插入,删除,修改。 
    对应的借口为: 
    objectInserted(ObjectInsertedEvent e); 
    objectRetracted(ObjectRetractedEvent e); 
    objectUpdated(ObjectUpdatedEvent e); 
    AgendEventListener是舰艇运行过程中Agenda管理调配规则发生的一些事件: 
    Action 在我理解应该是一个冲突就是上面提到过的 完全符合规则条件的,包含规则和数据的对象。 
    activationCancelled action被取消,可能是因为在规则的执行过程中,某个对象被修改或者某个对象被删除引起。 
    activationCreated 当有数据能匹配到规则,就能发生这个事件。 
    afterActivationFired 在规则执行后触发这个事件 
    agendaGroupPopped 规则组。。。 
    agendaGroupPushed 
    beforeActivationFired 在规则执行前触发这个事件 
    Drools4.0 自带一种非XML 格式的规则语言。这种格式通过使用标点符号,使得语言非常的轻量化,并且通过DSL(域规则语言)支持自然语言的扩展,这使得你可以将该语言演化到与你需要解决的问题域更为相配。 

    规则文件通常是以drl 扩展名结尾。在一个drl 文件中可以包含多个规则,函数等等。但是你也可以将规则分开到多个规则文件中(在这种情况下建议采用.rule 扩展名,但不是必需的),分散规则利于管理巨量规则的情况。DRL 是简单的text 文件格式。 
    完整的DRL文件结构是: 
    package package-name 
    imports 
    globals 
    functions 
    queries 
    rules 

    其中package是必须的。 
    Imports的含义和Java里的imports含义相似。 
    Globals表示全局的Fact但它并不进入匹配过程。 
    Functions queries rules就是我们要写的业务逻辑了,我们用的最多的就是Rules。 
    RULE的基本结构: 
    rule “ruleName” 
    when 
    <LHS> 
    Then 
            <RHS> 
    end 
    规则指定“when”作为一系列条件的集合(称为LHS),然后在“then”中指定一系列操作 
    (称为RHS)。(有些类似if…then) 
    不多介绍语法,介绍一下下面的例子: 
    rule "Purchase notification" 
        salience 10 
    when 
    $c : Customer() 
    $p : Purchase( customer == $c)     
    then 
        System.out.println( "Customer " + $c.name + " just purchased " + $p.product.name ); 
    end 
    第一行 Purchase notification是这个Rule的name,Rule name和Java的名称一样 在同一个包内不能重名。salience 10表示rule执行的优先级,当一个Fact进入WorkingMemory后可能有几个规则被触发,那你可以执行一个优先级来执行这些规则的执行顺序。 
    When下的部分是LHS, $c : Customer()表示触发规则的对象必须是Customer类型。并创建了一个Customer对象的引用 $c,$c可以在这个rule内使用。$p : Purchase( customer == $c)表示存在一个Purchase对象并且该对象的customer属性为上面描述的customer对象。如果符合上面两个条件规则引擎会执行then部分。Then部分的语法由于基本可以理解为Java语法。
  • 相关阅读:
    gdb coredum 信息例子
    leecode第一百一十四题(二叉树展开为链表)
    leecode第一百零一题(对称二叉树)
    leecode第九十四题(二叉树的中序遍历)
    leecode第七十五题(颜色分类)
    leecode第五十五题(跳跃游戏)
    leecode第四十九题(字母异位词分组)
    leecode第四十八题(旋转图像)
    leecode第三十四题(在排序数组中查找元素的第一个和最后一个位置)
    leecode第三十一题(下一个排列)
  • 原文地址:https://www.cnblogs.com/NL34/p/3555654.html
Copyright © 2020-2023  润新知