在 Drools 规则引擎中使用列表

Working with lists in Drools Rules Engine

我正在尝试使用 drools 中的列表。我正在传递一个包含购买清单的请求。我想做几条规则,包括检查尺寸是否正确,然后检查所有元素是否相同,如果所有购买都获得授权,......我有以下代码,但我 运行 在使用列表。这是正确的方法吗?特别是在检查尺寸时?

import com.rules.Purchase
import com.rules.PurchaseRequest

dialect  "mvel"

global Boolean eligibleForRefund

rule "Check for list not equal to two elements" salience 10
    when
        PurchaseRequest(getPurchases != null, getPurchases.size() != 2)

    then
        drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

rule "Check for two purchases" salience 9
    when:
        $purchaseRequest: PurchaseRequest()
        Number(intValue != 2) from accumulate(Purchase(getStatus() == "Approved") from $purchaseRequest.getPurchases(), count(1))

    then
        drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

rule "Check for the same purchases" salience 8
    when:
        $purchaseRequest: PurchaseRequest()

    then
        firstPurchase = $purchaseRequest.getPurchases().get(0).getCost();
        hasAllElements = true;

        for (Purchase purchase : $purchaseRequest.getPurchases()) {
            if (purchase.getCost() != firstPurchase) {
                hasAllElements = false;
            }
        }

        drools.getKieRuntime().setGlobal("eligibleForRefund", hasAllElements);
end

假设您的 class 定义如下所示:

class PurchaseRequest {
  private List<Purchase> purchases;
  
  public List<Purchase> getPurchases() { return this.purchases; }
}

您应该从 holder 中提取引用,而不是通过 getters 不断地与事物交互。在其他项目中,这有助于保持数据一致,尤其是共享资源。回想一下,如果您有一个名称与格式 getXyz 匹配的 getter,您可以简单地将其称为 xyz,并且 drools 会自动将其映射到 getter 函数。这允许我们通过 PurchaseRequest( $purchases: purchases ) 获得购买,因为 purchases 将映射到 getPurchases()。 (请注意,如果 purchases 碰巧是一个 public 变量,它会先映射到那个变量;但由于它是私有的,它会返回到遵循 bean 命名约定的 public getter。)

其次,您在一个非常简单的场景中使用了 accumulate,而 collect 可能更合适。一般来说,你会使用 accumulate 来处理更复杂的“得到看起来像这样的东西”之类的情况;但对于简单的匹配,collect 也同样有效。

第三条规则需要最多的工作。您 想在规则的右侧执行这种业务逻辑。有很多方法可以检查所有元素是否相同——如果你已经实现了 equals/hashCode 你可以把所有东西都推到一个集合中并确认集合的长度仍然是长度清单的;您可以颠倒规则,改为检查至少一项不同的项目;您可以使用 accumulate 或 collect; ...

终于——

  • 避免突出。他们是糟糕的设计。您的规则应该是独立的。您在这里只需要显着性,因为您的第三条规则同时设置了 true false。相反,如果您默认为 true,然后使用规则将其覆盖为 false,则您可以完全没有显着性。
  • 对全局变量使用基元是非常不寻常的。坦率地说,我不相信这甚至可以与原语一起使用。全局变量起作用是因为对象是通过引用传递的,并在规则中更新,因此保留对对象的引用的调用者将获得更新后的值。这不适用于原语。
rule "Check for list not equal to two elements" 
salience 1
when
  PurchaseRequest($purchases: purchases != null)
  List(size != 2) from $purchases
then
  drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

rule "Check for two purchases"
salience 1
when:
  PurchaseRequest( $purchases: purchases != null)
  List( size != 2 ) from collect( Purchase(status == "Approved") from $purchases)
then
  drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

// I've no idea what data type `getCost()` returns; I'm assuming "String"
rule "Check for the same purchases"
when:
  PurchaseRequest($purchases: purchases != null)

  // accumulate all of the costs into a set. if all costs are the same, set size = 1
  $costs: Set() from accumulate( Purchase( $cost: cost ) from $purchases;
                                 collectSet($cost))
then
  drools.getKieRuntime().setGlobal("eligibleForRefund", $costs.size() == 1);
end