Порядок выполнения правил Drools для атрибутов и содержащего объекта - PullRequest
0 голосов
/ 26 апреля 2019

Я немного борюсь с концепцией в Drools, кто-нибудь может прояснить, смотрю ли я на эту проблему неправильно?Решение работает, но похоже, что это неправильный подход, как я объясню в конце.

У меня есть объект модели Country, содержащий id, continent, area;где зона - это экономическая зона, например, ЕС или США;У меня есть перечисления для CountryID, EconomicAreaId, ContinentID;У меня есть объект запроса, содержащий происхождение: Страна, место назначения: Страна, 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 рассчитал доплату, упрощенную здесь как int.

Чтобы сделать это, я должен добавить оба объекта страны и выполнить все правилачтобы заполнить объекты страны, ТОГДА добавьте объект запроса и СНЯТЬ все правила СНОВА, чтобы получить доплату.

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 была концепция оценки всех правил в правильном порядке, поэтому он «знал», чтобы применять правила к странам, прежде чем использовать их в качестве входных данных для правил начисления.

Это только упрощенный пример Iя играю с таким, кажется, это не должно быть таким сложным, так что я неправильно понял концептуальный подход?

1 Ответ

2 голосов
/ 29 апреля 2019

О, 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 объекты. Слюни не будут автоматически понимать, что когда вы изменяете 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

Как видите, Друльс умен, но вы должны немного ему помочь;)


Лучшим подходом при работе с параметризованными значениями такого типа в соответствии с фиксированным набором условий является моделирование самого условия как факта:

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

Надеюсь, это поможет,

...