Drools學習筆記(四)---執行控制

議程

議程通過rete算法實現。它維護了多組規則的執行,並規劃這些規則的執行順序。

當操作處於RuleRuntime階段時,若規則完全匹配則有資格被執行,單一的規則執行後可能導致多個規則有資格被執行。當符合規則條件時,會將該規則放入議程。議程通過沖突解決策略來控制這些規則的執行順序。

發動機循環經過兩個階段:
1. 規則運行階段。大多數的工作在這裏進行,包括Consequence (RHS本身)和java main方法。一旦Consequence或是java main 調用了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
}

到目前爲止,您已經知道如何創建KieBase以及如何實例化填充KieSession,爲了更清晰地表示,我們將數據以表的形式展示。下表顯示爲此插入了一個Account實例。同時還插入了該賬戶兩個季度的貸方與借方現金流記錄。
這裏寫圖片描述

兩個規則可以被用來定義該季度貸方與借方並更新賬戶餘額。下面這兩條規則約束了規定時間段內某一個賬戶的現金流。注意”&&”使用了簡短的語法來避免的變量的重複命名。

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”將會影響一條數據。
這裏寫圖片描述
上圖兩個現金流表中展示的數據是與規則所匹配的,數據在插入階段是匹配的。這些規則不會立即執行,只有在fireAllRules()被調用後。同時,規則與它匹配的數據被放在議程中,作爲一個規則實例。議程中存放多條規則實例,在調用fireAllRules()後,纔會執行他們的結果。規則實例在議程中被稱爲一個衝突集,他們的執行由衝突解決策略決定。注意現在執行的順序被認爲是隨機的。
這裏寫圖片描述
當上面所有的規則被執行後,賬戶上的餘額變成了-25。
這裏寫圖片描述
如果AccountPeriod變成第二季度,我們只有一條匹配數據和一個規則實例在議程中。
規則執行後餘額變爲25。
這裏寫圖片描述
這裏寫圖片描述

衝突解決

如果你不想規則的執行順序是隨機的,該怎麼辦?當有多條規則實例在議程中時,他們有可能存在衝突,會有一個衝突解決策略被用來決定執行順序。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();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章