Drools学习笔记(四)---执行控制

议程

议程通过rete算法实现。它维护了多组规则的执行,并规划这些规则的执行顺序。

当操作处于RuleRuntime阶段时,若规则完全匹配则有资格被执行,单一的规则执行后可能导致多个规则有资格被执行。当符合规则条件时,会将该规则放入议程。议程通过冲突解决策略来控制这些规则的执行顺序。

发动机循环经过两个阶段:
1. 规则运行阶段。大多数的工作在这里进行,包括Consequence (RHS本身)和java main方法。一旦Consequence或是java main 调用了fireAllRules(),引擎将会切换到议程评估阶段
2. 议程评估阶段。该阶段试图选择一个规则去执行。如果没有合适的规则,则该阶段结束,否则将会执行一个合适的规则,并进入到规则运行阶段。
下图为两个阶段的执行过程:
Drools学习笔记(四)---执行控制_第1张图片

重复该过程直到议程清楚,在这种情况下,控制返回到调用应用程序。当正在执行规则运行时操作时,不会触发任何规则。

规则匹配和冲突集

现金流示例

到目前为止,数据和匹配过程一直很简单。为了混合起来,将探索一个新的例子来处理日期期间的现金流量计算。将在关键阶段说明性地显示发动机的状态,以帮助更好地了解发动机罩下的实际情况。将使用三个类,如下所示。这将有助于我们增加对模式匹配的理解并进一步加入。然后我们将使用它来说明执行控制的不同技术。

public class CashFlow {
    private Date   date;
    private double amount;
    private int    type;
    long           accountNo;
    // getter and setter methods here
}

public class Account {
    private long   accountNo;
    private double balance;
    // getter and setter methods here
}

public AccountPeriod {
    private Date start;
    private Date end;
    // getter and setter methods here
}

到目前为止,您已经知道如何创建KieBase以及如何实例化填充KieSession,为了更清晰地表示,我们将数据以表的形式展示。下表显示为此插入了一个Account实例。同时还插入了该账户两个季度的贷方与借方现金流记录。
Drools学习笔记(四)---执行控制_第2张图片

两个规则可以被用来定义该季度贷方与借方并更新账户余额。下面这两条规则约束了规定时间段内某一个账户的现金流。注意”&&”使用了简短的语法来避免的变量的重复命名。

rule "increase balance for credits"
when
  ap : AccountPeriod()
  acc : Account( $accountNo : accountNo )
  CashFlow( type == CREDIT,
            accountNo == $accountNo,
            date >= ap.start && <= ap.end,
            $amount : amount )
then
  acc.balance  += $amount;
end
rule "decrease balance for debits"
when
  ap : AccountPeriod()
  acc : Account( $accountNo : accountNo )
  CashFlow( type == DEBIT,
            accountNo == $accountNo,
            date >= ap.start && <= ap.end,
            $amount : amount )
then
  acc.balance -= $amount;
end

之前,我们展示了规则如何等同于SQL,以此帮助有SQL基础的人理解规则。这两个规则可以被表示为两个视图,每个视图配一个触发器,如下所示:

select * from Account acc,
              Cashflow cf,
              AccountPeriod ap
where acc.accountNo == cf.accountNo and
      cf.type == CREDIT and
      cf.date >= ap.start and
      cf.date <= ap.end

   trigger : acc.balance += cf.amount
select * from Account acc,
              Cashflow cf,
              AccountPeriod ap
where acc.accountNo == cf.accountNo and
      cf.type == DEBIT and
      cf.date >= ap.start and
      cf.date <= ap.end
trigger : acc.balance -= cf.amount

如果AccountPeriod被设置为第一个季度,那么规则”increase balance for credits”将会影响两条数据,规则“”decrease balance for debits”将会影响一条数据。
Drools学习笔记(四)---执行控制_第3张图片
上图两个现金流表中展示的数据是与规则所匹配的,数据在插入阶段是匹配的。这些规则不会立即执行,只有在fireAllRules()被调用后。同时,规则与它匹配的数据被放在议程中,作为一个规则实例。议程中存放多条规则实例,在调用fireAllRules()后,才会执行他们的结果。规则实例在议程中被称为一个冲突集,他们的执行由冲突解决策略决定。注意现在执行的顺序被认为是随机的。
这里写图片描述
当上面所有的规则被执行后,账户上的余额变成了-25。
这里写图片描述
如果AccountPeriod变成第二季度,我们只有一条匹配数据和一个规则实例在议程中。
规则执行后余额变为25。
Drools学习笔记(四)---执行控制_第4张图片
这里写图片描述

冲突解决

如果你不想规则的执行顺序是随机的,该怎么办?当有多条规则实例在议程中时,他们有可能存在冲突,会有一个冲突解决策略被用来决定执行顺序。Drools的策略非常简单,它会在规则中指定一个优先级。每个规则都有一个默认的优先级0,这个值越高优先级也就越高。

为了说明优先级我们添加一个规则来打印账户余额,我们希望这条规则在所有规则之后执行。我们对它赋予一个负值以保证他在所有默认值为0的规则后面。

rule "Print balance for AccountPeriod"
        salience -50
    when
        ap : AccountPeriod()
        acc : Account()
    then
        System.out.println( acc.accountNo + " : " + acc.balance );
end

下图描述在议程中规则的执行顺序。前三条借款与贷款规则为随机执行,打印余额则是在最后被执行。
这里写图片描述

议程组

议程组允许你将规则以组的形式存放,并将这些组放到堆栈上。堆栈具有push\pop操作。通过调用setFocus()将组放到栈上:

ksession.getAgenda().getAgendaGroup( "Group A" ).setFocus();

议程总是在栈的顶部开始执行。当一个议程组中的所有规则都被执行后,这个议程组会从栈顶移除然后执行下一组。

rule "increase balance for credits"
  agenda-group "calculation"
when
  ap : AccountPeriod()
  acc : Account( $accountNo : accountNo )
  CashFlow( type == CREDIT,
            accountNo == $accountNo,
            date >= ap.start && <= ap.end,
            $amount : amount )
then
  acc.balance  += $amount;
end
rule "Print balance for AccountPeriod"
  agenda-group "report"
when
  ap : AccountPeriod()
  acc : Account()
then
  System.out.println( acc.accountNo +  " : " + acc.balance );
end

根据栈“先进后出”的特性,先设置”report”,再设置”calculation”,以保证”calculation”被第一个执行。

Agenda agenda = ksession.getAgenda();
agenda.getAgendaGroup( "report" ).setFocus();
agenda.getAgendaGroup( "calculation" ).setFocus();
ksession.fireAllRules();

你可能感兴趣的:(Drools)