规则引擎 Easy Rules 使用实例(一)

那年仲夏 提交于 2020-02-26 16:35:53
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-core</artifactId>
    <version>3.2.0</version>
</dependency>

<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-support</artifactId>
    <version>3.2.0</version>
</dependency>

<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-mvel</artifactId>
    <version>3.2.0</version>
</dependency>

一、使用场景
       在编写代码过程中,我们对于if...else...语句是相当的熟悉了。但是如果条件分支比较多,比如有十几个、几十个、上百个,甚至更多时,如果我们还坚持在业务逻辑代码中使用if...else...语句,那么这个文件中的代码显得十分不协调,并且如果我们以后想再增加或减少规则时,还要来到这块业务逻辑代码中去修改if...else...语句,这给将来的维护带来了一定的不便。
        针对这种情况,我们可以考虑使用规则引擎来解决。规则引擎不只一种,本文介绍的是easy rule规则引擎的使用。对于上面的场景,easy rule会把if...else...中的逻辑从业务逻辑代码中提取出来,并把这些逻辑存放在其他文件中。这样做以后,业务逻辑代码看上去就清爽了很多。同时如果将来我们想要修改这些规则时,直接去找这些存放规则的文件就行了。在easy rule中,存放规则有两种方式,一种是使用 .java 文件,另外一种是使用 .yml 文件,下面我们分别介绍。

二、使用实例
       假设我们有这样一个场景:
        (1)如果一个数字可以被5整除,则输出“fizz”;
        (2)如果一个数字可以被7整除,则输出“buzz”;
        (3)如果一个数字可以同时被5和7整除,则输出“fizzbuzz”;
        (4)如果一个数字不满足以上三个条件,则输出这个数字本身。

1、不使用规则引擎的实现方式:

public class FizzBuzz {
  public static void main(String[] args) {
    for(int i = 1; i <= 100; i++) {
      if (((i % 5) == 0) && ((i % 7) == 0))
        System.out.print("fizzbuzz");
      else if ((i % 5) == 0) System.out.print("fizz");
      else if ((i % 7) == 0) System.out.print("buzz");
      else System.out.print(i);
      System.out.println();
    }
    System.out.println();
  }
}

2、将规则存放在 .java 文件中:
    (1).java 规则文件内容如下:

public class RuleClass {

    @Rule(priority = 1)
    public static class FizzRule {
        @Condition
        public boolean isFizz(@Fact("number") Integer number) {
            return number % 5 == 0;
        }

        @Action
        public void printFizz() {
            System.out.print("fizz");
        }
    }

    @Rule(priority = 2)
    public static class BuzzRule {
        @Condition
        public boolean isBuzz(@Fact("number") Integer number) {
            return number % 7 == 0;
        }

        @Action
        public void printBuzz() {
            System.out.print("buzz");
        }
    }

    public static class FizzBuzzRule extends UnitRuleGroup {

        public FizzBuzzRule(Object... rules) {
            for (Object rule : rules) {
                addRule(rule);
            }
        }

        @Override
        public int getPriority() {
            return 0;
        }
    }

    @Rule(priority = 3)
    public static class NonFizzBuzzRule {

        @Condition
        public boolean isNotFizzNorBuzz(@Fact("number") Integer number) {
            // can return true, because this is the latest rule to trigger according to
            // assigned priorities
            // and in which case, the number is not fizz nor buzz
            return number % 5 != 0 || number % 7 != 0;
        }

        @Action
        public void printInput(@Fact("number") Integer number) {
            System.out.print(number);
        }
    }

}

        (2)客户端调用代码如下:

public class RuleClient {
    public static void main(String[] args) {
        // create a rules engine
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
        RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);

        // create rules
        Rules rules = new Rules();
        rules.register(new FizzRule());
        rules.register(new BuzzRule());
        rules.register(new RuleClass.FizzBuzzRule(new RuleClass.FizzRule(), new RuleClass.BuzzRule()));
        rules.register(new NonFizzBuzzRule());

        // fire rules
        Facts facts = new Facts();
        for (int i = 1; i <= 100; i++) {
            facts.put("number", i);
            fizzBuzzEngine.fire(rules, facts);
            System.out.println();
        }
    }

}

代码注解:
注解1

RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);

        这行代码的作用是为接下来的RulesEngine的创建设置属性。这里只设置了一个属性skipOnFirstAppliedRule,意思是在之后执行RuleClass中的规则时,只要有一个规则被触发,则当前被传进来的Fact就不再判断是否满足其他规则的条件。
        像这样的属性还有几个,我们在接下来的文章中将会讲到。

注解2
        我们要注意Facts的使用。Facts的用法很像Map,它是客户端和规则文件之间通信的桥梁。在客户端使用put方法向Facts中添加数据,在规则文件中通过key来得到相应的数据。

3、将规则文件存放在 .yml 文件中:
(1).yml 规则文件内容如下:

---
name: "fizz rule"
description: "print fizz if the number is multiple of 5"
priority: 1
condition: "number % 5 == 0"
actions:
  - "System.out.println(\"fizz\")"
  
---
name: "buzz rule"
description: "print buzz if the number is multiple of 7"
priority: 2
condition: "number % 7 == 0"
actions:
  - "System.out.println(\"buzz\")"
  
---
name: "fizzbuzz rule"
description: "print fizzbuzz if the number is multiple of 5 and 7"
priority: 0
condition: "number % 5 == 0 && number % 7 == 0"
actions:
  - "System.out.println(\"fizzbuzz\")"
  
---
name: "non fizzbuzz rule"
description: "print the number itself otherwise"
priority: 3
condition: "number % 5 != 0 || number % 7 != 0"
actions:
  - "System.out.println(number)"

        (2)客户端调用代码如下:

public class RuleClient {
   public static void main(String[] args) throws FileNotFoundException {
       // create a rules engine
       RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
       RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);

       // create rules
       Rules rules = MVELRuleFactory.createRulesFrom(new FileReader("fizzbuzz.yml"));

       // fire rules
       Facts facts = new Facts();
       for (int i = 1; i <= 100; i++) {
           facts.put("number", i);
           fizzBuzzEngine.fire(rules, facts);
           System.out.println();
       }
   }

}

执行结果如下:

1
2
3
4
fizz
6
buzz
8
9
fizz
11
12
13
buzz
fizz
16
17
18
19
fizz
buzz
22
23
24
fizz
26
27
buzz
29
fizz
31
32
33
34
fizzbuzz
36
37
38
39
fizz
41
buzz
43
44
fizz
46
47
48
buzz
fizz
51
52
53
54
fizz
buzz
57
58
59
fizz
61
62
buzz
64
fizz
66
67
68
69
fizzbuzz
71
72
73
74
fizz
76
buzz
78
79
fizz
81
82
83
buzz
fizz
86
87
88
89
fizz
buzz
92
93
94
fizz
96
97
buzz
99
fizz

相关文档

Easy Rules Wiki

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!