JavaルールエンジンEasy Rulesの使用紹介


1.Easy Rulesの概要
Easy RulesはJavaのルールエンジンで、「Shuld I use a Rules Egine?」という文章からインスピレーションを受けました。
ルールエンジンとは、任意の計算モデルを提供することです。通常の命令式モデル(条件と循環を伴う命令で順次構成されている)とは異なり、ルールエンジンは生産規則システムに基づいている。これは生産規則のセットです。各規則には一つの条件と一つの動作があります。
正確なところはルールはどの順番で作成できますか?エンジンは順序に意味があるどんな方法で計算しますか?その良い方法を考えると、システムがすべての規則を実行し、条件が成立する規則を選択し、その後に対応する操作を実行することです。このようにするメリットは、多くの問題がこのモデルに自然に当てはまります。
if car.owner.hasCelPhone then premium+=100;
if car.model.theftRating>4 then premium+200;
if car.owner.lives InDodyArea&car.model.theftRating>2 then premium+=300;
規則エンジンはこのような計算モデルのプログラミングをより容易にするツールである。これは完全な開発環境かもしれません。あるいは伝統的なプラットフォームで働くことができるフレームワークです。生産規則計算モデルは、一部の計算問題のみを解決するのに最適であり、従って、ルールエンジンはより大きなシステムに組み込むことができる。
自分で簡単なルールエンジンを作ることができます。あなたが必要とするのは、条件と動作を持つオブジェクトのセットを作成し、それらをセットに保存し、条件を評価して実行します。
Easy Rulesは、条件と動作を評価するためのルールを作成するためにRule抽象を提供し、Rule Engine APIを提供します。
Easy Rulesは簡単で使いやすいです。二歩だけ必要です。
まず、ルールを定義します。方式はいろいろあります。
方式一:コメント

@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {

  @Condition
  public boolean itRains(@Fact("rain") boolean rain) {
    return rain;
  }
  
  @Action
  public void takeAnUmbrella() {
    System.out.println("It rains, take an umbrella!");
  }
}
方式二:チェーン式プログラミング

Rule weatherRule = new RuleBuilder()
    .name("weather rule")
    .description("if it rains then take an umbrella")
    .when(facts -> facts.get("rain").equals(true))
    .then(facts -> System.out.println("It rains, take an umbrella!"))
    .build();
方式3:式

Rule weatherRule = new MVELRule()
    .name("weather rule")
    .description("if it rains then take an umbrella")
    .when("rain == true")
    .then("System.out.println(\"It rains, take an umbrella!\");");
方式四:ymlプロファイル
例えば:weat her-rule.yml

name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
 - "System.out.println(\"It rains, take an umbrella!\");"

MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));
次に、ルールを適用します。

public class Test {
  public static void main(String[] args) {
    // define facts
    Facts facts = new Facts();
    facts.put("rain", true);

    // define rules
    Rule weatherRule = ...
    Rules rules = new Rules();
    rules.register(weatherRule);

    // fire rules on known facts
    RulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.fire(rules, facts);
  }
}
入門事例:ハローEasy Rules

<dependency>
  <groupId>org.jeasy</groupId>
  <artifactId>easy-rules-core</artifactId>
  <version>4.0.0</version>
</dependency>
骨組みを通してmavenプロジェクトを作成します。

mvn archetype:generate \
  -DarchetypeGroupId=org.jeasy \
  -DarchetypeArtifactId=easy-rules-archetype \
  -DarchetypeVersion=4.0.0
デフォルトで私たちにハローワールドRuleルールを生成しました。

package com.cjs.example.rules;

import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Rule;

@Rule(name = "Hello World rule", description = "Always say hello world")
public class HelloWorldRule {

  @Condition
  public boolean when() {
    return true;
  }

  @Action
  public void then() throws Exception {
    System.out.println("hello world");
  }

}

2.ルール定義
2.1.定義規則
大多数の業務規則は以下の定義で表現できます。
  • Name:名前空間における唯一のルール名
  • Description:ルールの概要
  • Priority:他の規則に対する優先度
  • Facts:事実、すぐに処理されるデータ
  • Contingtions:規則を適用するために必要な条件のセット
  • アクション:条件が満たされたときに実行される動作のセット

  • Easy Rulesはキーポイントごとに抽象的な業務ルールを定義します。
    Easy Rulesでは、Ruleインターフェースはルールを表します。
    
    public interface Rule {
    
      /**
      * This method encapsulates the rule's conditions.
      * @return true if the rule should be applied given the provided facts, false otherwise
      */
      boolean evaluate(Facts facts);
    
      /**
      * This method encapsulates the rule's actions.
      * @throws Exception if an error occurs during actions performing
      */
      void execute(Facts facts) throws Exception;
    
      //Getters and setters for rule name, description and priority omitted.
    
    }
    evaluate方法は、規則をトリガするためにTRUEに対して結果を計算しなければならない条件をカプセル化している。executeメソッドは、規則条件を満たすために実行すべき動作をパッケージ化しました。条件と動作はContinationとアクションインターフェースによって表される。
    定義規則には2つの方法があります。
  • POJOクラスに注釈を追加することにより
  • は、RuleBuider APIによってプログラミングされた
  • である。
    POJOクラスに@Rule注を追加することができます。例えば、
    
    @Rule(name = "my rule", description = "my rule description", priority = 1)
    public class MyRule {
    
      @Condition
      public boolean when(@Fact("fact") fact) {
        //my rule conditions
        return true;
      }
    
      @Action(order = 1)
      public void then(Facts facts) throws Exception {
        //my actions
      }
    
      @Action(order = 2)
      public void finally() throws Exception {
        //my final actions
      }
    }
    @Condition注解指定規則条件
    @Factコメント指定パラメータ
    @アクションコメント指定規則の実行動作
    RuleBuiderはチェーンスタイル定義規則をサポートしています。例えば、
    
    Rule rule = new RuleBuilder()
            .name("myRule")
            .description("myRuleDescription")
            .priority(3)
            .when(condition)
            .then(action1)
            .then(action2)
            .build();
    組み合わせルール
    CompsiteRuleはルールのセットからなります。これは典型的に設計モードを組み合わせる実現である。
    結合規則は、異なる方法で結合規則をトリガすることができるので、抽象的な概念である。
    Easy Rulesは3種類のCompsiteRuleを持参して実現します。
  • UnityRuleGroup:すべての規則を適用するか、または任意のルール(AND論理)を適用しない
  • Activation RuleGroup:最初の適用規則をトリガし、グループ内の他の規則(XOR論理)
  • を無視する。
  • Coditional RuleGroup:最も優先度の高い規則計算結果がtrueであれば、残りの規則
  • がトリガされる。
    複合規則は、基本ルールから作成して、従来のルールとして登録することができます。
    
    //Create a composite rule from two primitive rules
    UnitRuleGroup myUnitRuleGroup = new UnitRuleGroup("myUnitRuleGroup", "unit of myRule1 and myRule2");
    myUnitRuleGroup.addRule(myRule1);
    myUnitRuleGroup.addRule(myRule2);
    
    //Register the composite rule as a regular rule
    Rules rules = new Rules();
    rules.register(myUnitRuleGroup);
    
    RulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.fire(rules, someFacts);
    ルールごとに優先順位があります。登録規則をトリガするデフォルトの順序を表します。デフォルトの場合、より低い値はより高い優先度を表します。カスタマイズ優先度ポリシーを提供するために、compreTo方法を書き換えることができる。
    2.2.定義事実
    Easy Rulesでは、Fact APIは事実を表しています。
    
    public class Fact<T> {
       private final String name;
       private final T value;
    }

    くりを一つあげる:
    
    Fact<String> fact = new Fact("foo", "bar");
    Facts facts = new Facts();
    facts.add(fact);
    あるいは、このように簡単に書いてもいいです。
    
    Facts facts = new Facts();
    facts.put("foo", "bar");
    @Fact注でFactsをconditionとaction方法に注入できます。
    
    @Rule
    class WeatherRule {
    
      @Condition
      public boolean itRains(@Fact("rain") boolean rain) {
        return rain;
      }
    
      @Action
      public void takeAnUmbrella(Facts facts) {
        System.out.println("It rains, take an umbrella!");
        // can add/remove/modify facts
      }
    
    }
    2.3.ルールエンジンの定義
    Easy Rulesは二つのRulesEngineインターフェースの実現を提供します。
  • DefaultRulesEngine:規則の自然な順序に従って規則を適用します。
  • InferenceRulesEngine:既知の事実に対して規則を適用し続け、ルールが適用されなくなるまで
  • ルールエンジンを作成:
    
    RulesEngine rulesEngine = new DefaultRulesEngine();
    
    // or
    
    RulesEngine rulesEngine = new InferenceRulesEngine();
    その後、登録規則
    
    rulesEngine.fire(rules, facts);
    ルールエンジンにはいくつかの設定可能なパラメータがあります。

    くりを一つあげる:
    
    RulesEngineParameters parameters = new RulesEngineParameters()
      .rulePriorityThreshold(10)
      .skipOnFirstAppliedRule(true)
      .skipOnFirstFailedRule(true)
      .skipOnFirstNonTriggeredRule(true);
    
    RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
    2.4.ルールモニターの定義
    Rule Listenerインターフェースを実現することにより
    
    public interface RuleListener {
    
      /**
       * Triggered before the evaluation of a rule.
       *
       * @param rule being evaluated
       * @param facts known before evaluating the rule
       * @return true if the rule should be evaluated, false otherwise
       */
      default boolean beforeEvaluate(Rule rule, Facts facts) {
        return true;
      }
    
      /**
       * Triggered after the evaluation of a rule.
       *
       * @param rule that has been evaluated
       * @param facts known after evaluating the rule
       * @param evaluationResult true if the rule evaluated to true, false otherwise
       */
      default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) { }
    
      /**
       * Triggered on condition evaluation error due to any runtime exception.
       *
       * @param rule that has been evaluated
       * @param facts known while evaluating the rule
       * @param exception that happened while attempting to evaluate the condition.
       */
      default void onEvaluationError(Rule rule, Facts facts, Exception exception) { }
    
      /**
       * Triggered before the execution of a rule.
       *
       * @param rule the current rule
       * @param facts known facts before executing the rule
       */
      default void beforeExecute(Rule rule, Facts facts) { }
    
      /**
       * Triggered after a rule has been executed successfully.
       *
       * @param rule the current rule
       * @param facts known facts after executing the rule
       */
      default void onSuccess(Rule rule, Facts facts) { }
    
      /**
       * Triggered after a rule has failed.
       *
       * @param rule the current rule
       * @param facts known facts after executing the rule
       * @param exception the exception thrown when attempting to execute the rule
       */
      default void onFailure(Rule rule, Facts facts, Exception exception) { }
    
    }
    3.例
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.cjs.example</groupId>
      <artifactId>easy-rules-quickstart</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <packaging>jar</packaging>
      <dependencies>
        <dependency>
          <groupId>org.jeasy</groupId>
          <artifactId>easy-rules-core</artifactId>
          <version>4.0.0</version>
        </dependency>
        <dependency>
          <groupId>org.jeasy</groupId>
          <artifactId>easy-rules-support</artifactId>
          <version>4.0.0</version>
        </dependency>
        <dependency>
          <groupId>org.jeasy</groupId>
          <artifactId>easy-rules-mvel</artifactId>
          <version>4.0.0</version>
        </dependency>
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-simple</artifactId>
          <version>1.7.30</version>
        </dependency>
      </dependencies>
    </project>

    4.拡張
    規則は本質的に、y=f(x 1,x 2,.,xn)のような関数です。
    ルールエンジンは、業務コードと業務規則の分離を解決するためのエンジンであり、アプリケーションに組み込まれたコンポーネントであり、業務決定をアプリケーションコードから分離することを実現しています。
    もう一つの一般的な方法はJava+Grovyによって実現され、Javaに埋め込まれたGrouvyスクリプトエンジンが業務ルールを剥離します。
    https://github.com/j-easy/easy-rules/wiki
    ここで、JavaルールエンジンEasy Rulesの使用について紹介した文章を紹介します。JavaルールエンジンEasy Rulesの内容については、以前の文章を検索してください。または下記の関連記事を引き続きご覧ください。これからもよろしくお願いします。