【Drools学习笔记】08-执行控制

议程是一个Rete功能。它维护着一组能够执行的规则,其工作是以确定的顺序安排执行。

在对行动采取行动时RuleRuntime,规则可能完全匹配并有资格执行; 单个规则运行时操作可以生成多个符合条件的规则。当规则完全匹配时,将创建规则匹配,引用规则和匹配的事实,并将其置于议程中。议程使用冲突解决策略控制这些匹配的执行顺序。

规则引擎反复循环两个阶段:

  1. 规则运行时操作。这是大多数工作发生的地方,无论是在Consequence(RHS本身)还是主要的Java应用程序进程中。一旦结果完成或主Java应用程序进程调用fireAllRules()引擎切换到议程评估阶段。

  2. 议程评估。这会尝试选择要触发的规则。如果没有找到规则则退出,否则它会触发找到的规则,将阶段切换回规则运行时操作。

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

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
}

以下两条规则限制了特定时间段内帐户的现金流量。注意“&&”使用快捷语法来避免重复两次字段名称。 规则文件:

package org.drools.examples.cashflow;
dialect  "mvel"

rule "Increase balance for AccountPeriod Credits"
when
   ap : AccountPeriod( )
   $acc : Account( )
   cf : CashFlow( type == CashFlowType.CREDIT,
             accountNo == $acc.accountNo,
             date >= ap.start && <= ap.end )
then
   $acc.balance = $acc.balance + cf.amount;
end

rule "Decrease balance for AccountPeriod Debits"
when
   ap : AccountPeriod( )
   $acc : Account( )
   cf : CashFlow( type == CashFlowType.DEBIT, accountNo == $acc.accountNo, date >= ap.start && <= ap.end )
then
   $acc.balance = $acc.balance - cf.amount;
end

上面两条规则可以使用下面的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
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
trigger : acc.balance -= cf.amount

如果AccountPeriod设置为第一季度,我们约束规则“增加信用余额”以触发两行数据并“减少借记余额”以对一行数据进行操作。

tables2

上面的两个现金流表表示两个规则的匹配数据。数据在插入阶段匹配,正如您在上一章中发现的那样,不会直接触发,而是仅在fireAllRules()调用之后触发同时,规则及其匹配数据被放在议程上,称为Ruie匹配或规则实例。议程是一个规则匹配表,只要调用fireAllRules(),就可以触发并执行其后果。议程上的规则匹配被称为冲突集 ,其执行由冲突解决策略决定。请注意,到目前为止执行的顺序被认为是任意的。

tables7

在触发所有上述激活后,该帐户的余额为-25。

tables3

如果AccountPeriod更新到第二季度,我们只有一个匹配的数据行,因此在议程上只有一个规则匹配。

激活该激活会导致25的余额。

tables4
tables5
解决冲突

如果您不希望规则执行的顺序是任意的,该怎么办?当议程中存在一个或多个规则匹配时,它们被认为存在冲突,并且冲突解决策略用于确定执行的顺序。Drools策略非常简单,基于salience值,为规则赋予优先级。每个规则的默认值为0,值越高,优先级越高。

作为一般规则,最好不要依赖以任何特定顺序触发的规则,并且在不担心“流程”的情况下编写规则。然而,当流程需要特定的顺序时,除了使用salience之外还存在其他功能:议程组,规则流组,激活组和控制/信号量事实。

为了说明Salience,我们添加了一个规则来打印帐户余额,我们希望在所有帐户的所有借记和贷记都应用之后执行此规则。我们通过为此规则指定负面显着性来实现此目的,以便在具有默认显着性0的所有规则之后触发。

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

下表描述了由此产生的议程。三个借记和贷记规则显示为任意顺序,而打印规则排在最后,以后执行。

tables6

以上描述的java代码实现。
package org.drools.examples.cashflow;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;

import java.text.SimpleDateFormat;
import java.util.Date;

public class CashFlowMain {

    public static void main(String[] args) throws Exception {
        KieContainer kc = KieServices.Factory.get().getKieClasspathContainer();
        KieSession ksession = kc.newKieSession( "CashFlowKS");
        AccountPeriod acp = new AccountPeriod(date( "2013-01-01"), date( "2013-03-31"));
        Account ac = new Account(1, 0);

        CashFlow cf1 = new CashFlow(date( "2013-01-12"), 100, CashFlowType.CREDIT, 1 );
        CashFlow cf2 = new CashFlow(date( "2013-02-2"), 200, CashFlowType.DEBIT, 1 );
        CashFlow cf3 = new CashFlow(date( "2013-05-18"), 50, CashFlowType.CREDIT, 1 );
        CashFlow cf4 = new CashFlow(date( "2013-03-07"), 75, CashFlowType.CREDIT, 1 );

        FactHandle fh = ksession.insert( acp );
        ksession.insert( ac );

        ksession.insert( cf1 );
        ksession.insert( cf2 );
        ksession.insert( cf3 );
        ksession.insert( cf4 );

        ksession.fireAllRules();

        acp.setStart(date( "2013-04-01"));
        acp.setEnd(date( "2013-06-31"));
        ksession.update(fh, acp);

        ksession.fireAllRules();
    }

    public static Date date(String str) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.parse( str );
    }
}
运行结果
Account Number 1 balance -25
Account Number 1 balance 25

议程组

议程组允许您将规则放入组中,并将这些组放入堆栈中。堆栈有push / pop bevaviour。调用“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
Agenda agenda = ksession.getAgenda();
agenda.getAgendaGroup( "report" ).setFocus();
agenda.getAgendaGroup( "calculation" ).setFocus();
ksession.fireAllRules();


猜你喜欢

转载自blog.csdn.net/shenchaohao12321/article/details/80916708
今日推荐