在属性和包含对象上执行Drools规则的顺序

时间:2019-04-26 19:26:03

标签: drools

我在Drools中苦苦挣扎,有人能澄清我是否以错误的方式看待这个问题吗?该解决方案有效,但似乎是错误的方法,正如我将在最后解释的那样。

我有一个包含ID,洲,区域的Country模型对象;区域是经济区域,例如欧盟或美国; 我有CountryID,EconomicAreaId,ContinentID的枚举; 我有一个Request对象,包含origin:Country,destination:Country,srucharge:Int; 我有两个Drools规则(实际上以xls表示,但我将在此处列出drl):

<package and imports are correct>
rule "UK"
    when
        $c:Country($c.getId() in (CountryID.UNITED_KINGDOM))
    then
        $c.setArea(EconomicAreaID.EU);
        $c.setContinent(ContinentID.EUROPE);
        /* update($c); // I tried this too :) //
end

rule "US"
    when
        $c:Country($c.getId() in (CountryID.UNITED_STATES))
    then
        $c.setArea(EconomicAreaID.US);
        $c.setContinent(ContinentID.N_AMERICA);
end

rule "EU to EU"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.EU, $rr.getDestination.getArea() == EconomicAreaID.EU)
    then
        $rr.setCharge(1);
end

rule "US to US"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.US, $rr.getDestination.getArea() == EconomicAreaID.US)
    then
        $rr.setCharge(2);
end

rule "EU to US"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.EU, $rr.getDestination.getArea() == EconomicAreaID.US)
    then
        $rr.setCharge(3);
end

rule "US to EU"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.US, $rr.getDestination.getArea() == EconomicAreaID.EU)
    then
        $rr.setCharge(4);
end

首先,我想插入两个国家对象,即起源和目的地,并让Drools自动将大陆和经济区分配给它们的属性;

然后我要Drools计算附加费,这里简化为整数。

为此,我必须同时添加国家对象并触发所有规则以填充国家对象,然后添加请求对象并再次触发所有规则以获取附加费用。

KieSession kSession = kieContainer.newKieSession();
ChargeRequest rr = new ChargeRequest(new Country("UNITED_STATES"), new Country("UNITED_KINGDOM"));

kSession.insert(rr.getOrigin()); // add both country objects
kSession.insert(rr.getDestination());
kSession.fireAllRules(); // LINE 6 : fire rules to calculate the economic areas and continents of the country objects

kSession.insert(rr); // add the ChargeRequest object
kSession.fireAllRules(); // fire the rules to calculate the charge

return rr.toString(); // ChargeRequest(origin=Country(id=UNITED_STATES, continent=N_AMERICA, area=US), destination=Country(id=UNITED_KINGDOM, continent=EUROPE, area=EU), charge=4)

如果我省略第6行,则费用不会计算为4,因为Drools对人口不足的国家对象执行费用规定。我以为Drools具有按正确顺序评估所有规则的概念,因此在将规则用作收费规则的输入之前,它会“知道”将规则应用于各个国家/地区。

这只是我正在使用的简化示例,因此似乎不应该这么复杂,所以我弄错了概念方法吗?

1 个答案:

答案 0 :(得分:2)

哦,Drools知道如何以及何时评估您的规则,请确保确实如此。唯一的是,当模型中的某些内容发生变化时,您必须让它知道。 您在规则右侧调用的设置器永远不会通知Drools,因此它不知道您所做的更改。

Drools中有一个名为modify的特殊关键字,可以在模型中发生更改时通知引擎。

设置区域和大陆的规则应如下所示(具体取决于您正在执行的Drools版本,使用modify时可能会遇到无限循环。我已经改写了左侧您可以避免的规则):

rule "UK"
when
    $c:Country(
      area == null,
      continent == null,
      id in (CountryID.UNITED_KINGDOM)
    )
then
    modify($c){
      setArea(EconomicAreaID.EU),
      setContinent(ContinentID.EUROPE)
    };
end

现在,第二个问题是,在设置费用的规则中,Country嵌套在ChargeRequest对象中。 Drools不会自动理解,当您修改Country时,它必须重新评估与ChargeRequests相关的规则。您必须明确指出这一点:

rule "EU to EU"
when
    $rr:ChargeRequest(
      $origin: origin, 
      $destination: destination
    )
    Country(
      this == $origin,
      area == EconomicAreaID.EU
    )
    Country(
      this == $destination,
      area == EconomicAreaID.EU
    )
then
    $rr.setCharge(1);
end

如您所见,Drools很聪明,但是您需要稍微帮助他;)


根据一组固定的条件处理此类参数化值时,更好的方法是将条件本身建模为事实:

1.-创建一个类来对约束和结果进行建模:

public class RequestCharge{
  private EconomicAreaID origin;
  private EconomicAreaID destination;
  private double charge;

  //getters and setters
}

2.-将所有约束插入会话:

kSession.insert(new RequestCharge(EconomicAreaID.EU, EconomicAreaID.EU, 1.0);
kSession.insert(new RequestCharge(EconomicAreaID.US, EconomicAreaID.US, 2.0);
kSession.insert(new RequestCharge(EconomicAreaID.EU, EconomicAreaID.US, 3.0);
kSession.insert(new RequestCharge(EconomicAreaID.US, EconomicAreaID.EU, 4.0);

3.-有一条规则来处理所有请求(您可能想添加一条额外的规则来处理与任何已配置的RequestCharges不匹配的请求):

rule "Apply Charge"
when
    $rr:ChargeRequest(
      $origin: origin, 
      $destination: destination
    )
    $o: Country(  //You still need these Country patterns to react to the changes in the first rule
      this == $origin
    )
    $d: Country(
      this == $destination
    )
    RequestCharge(
      origin == $o,
      destination == $d,
      $charge: charge
    )
then
    $rr.setCharge($charge);
end

希望有帮助,