Easy Rules

一.什么是Easy Rules

Easy Rules 是一款 Java 规则引擎,它的诞生启发自有Martin Fowler 一篇名为 "Should I use a Rules Engine?" 文章。
该文文章写道:您可以自己构建一个简单的规则引擎。您所需要的就是创建一组具有条件和操作的对象,将它们存储在集合中,并遍历它们以评估条件并执行操作。
这正是Easy Rules所做的,它提供了规则抽象来创建带有条件和操作的规则,以及运行一组规则来评估条件和执行操作的RulesEngine API。

二.核心特性

  • 轻量级库和易于学习的API;
  • 基于POJO的注释编程模型开发;
  • 用于定义业务规则并使用Java轻松应用它们的有用抽象;
  • 能够从原始规则创建复合规则;
  • 使用表达式语言定义规则的能力;

三.运行环境

EasyRules是一个Java库。它需要Java 1.7+运行环境。

四.maven坐标


    org.jeasy
    easy-rules-core
    3.2.0



    org.jeasy
    easy-rules-support
    3.2.0



    org.jeasy
    easy-rules-mvel
    3.2.0


五.

1.名词解释

  • Name:规则名称空间中的唯一规则名称
  • Description:规则的简要说明
  • Priority:规则优先级
  • Facts:规则运行时已知的一些数据
  • Conditions:为了应用规则,应根据某些事实应满足的条件集
  • Actions:满足条件时要执行的操作集(可以添加/删除/修改事实)

2.定义Facts
Facts API是一组规则需要检验的facts的抽象。在内部,Facts持有HashMap,这意味着:

  • Facts 命名必须唯一且不能为空;
  • 任何Java对象都可以当做Fact.

说白了 Facts 就是一个 HashMap,里面都是一些需要校验规则的参数字段.
下面是一个定义Facts例子:

Facts facts = new Facts();
facts.add("rain", true);

Facts可以用在condition 和action 方法中,在参数前面使用@Fact注解:

@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
    }
}

3.规则引擎
3.1 从3.1版开始,Easy Rules提供了规则引擎接口的两种实现:

  • DefaultRulesEngine:根据它们的自然顺序(默认优先级)应用规则;
  • InferenceRulesEngine:对已知事实持续应用规则,直到不再适用任何规则。

3.2 创建规则引擎
要创建规则引擎,可以使用每个实现的构造函数:

RulesEngine rulesEngine = new DefaultRulesEngine();

// or

RulesEngine rulesEngine = new InferenceRulesEngine();

然后,您可以按照以下方式运行注册规则:

rulesEngine.fire(rules, facts);

3.3 规则引擎参数

  • skipOnFirstAppliedRule参数告诉引擎在应用规则时跳过下一个规则。
  • skipOnFirstFailedRule参数告诉引擎在规则失败时跳过下一个规则。
  • skipOnFirstNonTriggeredRule参数告诉引擎跳过下一个规则,没有触发规则。
  • rulePriorityThreshold参数告诉引擎在优先级超过定义的阈值时跳过下一个规则。

您可以使用RulesEngineParameters API指定这些参数:

RulesEngineParameters parameters = new RulesEngineParameters()
    .rulePriorityThreshold(10)
    .skipOnFirstAppliedRule(true)
    .skipOnFirstFailedRule(true)
    .skipOnFirstNonTriggeredRule(true);
    
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

如果您想从引擎获得参数,可以使用以下代码片段:

RulesEngineParameters parameters = myEngine.getParameters();

六.HelloWorld

1.使用注释定义规则
Easy Rules提供了可以将POJO转换为规则的@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");
    }

}

@Condition注解标记了执行来评估规则条件的方法。这个方法必须是公共的,可能有一个或多个参数注释@Fact并返回一个布尔类型。只有一个方法可以用@Condition注释进行注释。

@Action注释标记用于执行规则操作的方法。规则可以有多个操作。可以使用order属性以指定的顺序执行操作。默认情况下,动作的顺序是0。

运行策略

public class Launcher {

    public static void main(String[] args) {

        // create facts
        Facts facts = new Facts();

        // create rules
        Rules rules = new Rules();
        rules.register(new HelloWorldRule());

        // create a rules engine and fire rules on known facts
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);

    }
}

七.复合规则

EasyRules允许您从原始规则创建复杂规则。CompositeRule是由一组规则组成的。

复合规则是一个抽象概念,因为组合规则可以以不同的方式触发。在3.2版中,Easy Rules附带了3种组合规则的实现:

UnitRuleGroup:单元规则组是作为一个单元的复合规则:要么应用所有规则,要么什么都不应用。
ActivationRuleGroup:激活规则组是一个复合规则,它触发第一个适用规则,并忽略组中的其他规则(XOR逻辑)。规则首先按照组内的自然顺序(默认优先级)进行排序。
ConditionalRuleGroup:条件规则组是一个复合规则,其中优先级最高的规则充当条件:如果优先级最高的规则求值为true,则触发其余规则。

八.Fizz Buzz

本教程使用简单的规则实现FizzBuzz应用程序。FizzBuzz是一个简单的应用程序,需要从1数到100,并且:

  • 如果数字是5的倍数,则打印“fizz”;
  • 如果数字是7的倍数,请打印“buzz”;
  • 如果数字是5和7的倍数,请打印“fizzbuzz”;
  • 否则打印数字本身.

下面是一个来自Java示例的FizzBuzz示例:

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();
  }
}

我们将为每个需求编写一个规则:
FizzRule

@Rule(name = "FizzRule", description = "FizzRule description", priority = 1)
public class FizzRule {

    @Condition
    public boolean isFizz(@Fact("number") Integer number) {
        return number % 5 == 0;
    }

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

BuzzRule

@Rule(name = "BuzzRule", description = "BuzzRule description", priority = 2)
public class BuzzRule {

    @Condition
    public boolean isBuzz(@Fact("number") Integer number) {
        return number % 7 == 0;
    }

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

FizzBuzzRule

public class FizzBuzzRule extends UnitRuleGroup {

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

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

NonFizzBuzzRule

@Rule
public 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);
    }

    @Priority
    public int getPriority() {
        return 3;
    }
}

FizzBuzzWithEasyRules

public class FizzBuzzWithEasyRules {
    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 FizzBuzzRule(new FizzRule(), new 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
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)