在 Easy Rules规则引擎(1-基础篇) 中我们已经简单介绍了Easy Rules
规则引擎的使用示例,这节我们详解介绍一下规则引擎的相关参数配置实例还有组合规则。
Easy Rules
规则引擎支持下面参数配置:
参数名称 | 参数类型 | 必选 | 默认值 |
---|---|---|---|
rulePriorityThreshold | int | 否 | Integer.MAX_VALUE |
skipOnFirstAppliedRule | boolean | 否 | false |
skipOnFirstFailedRule | boolean | 否 | false |
skipOnFirstNonTriggeredRule | boolean | 否 | false |
skipOnFirstAppliedRule
: 当规则被触发并且成功执行行为后是否跳过下条规则。skipOnFirstFailedRule
: 当判断规则是否触发抛出异常或者触发成功但行为执行后抛出异常是否跳过下条规则。skipOnFirstNonTriggeredRule
: 当规则未被触发是否跳过下条规则。rulePriorityThreshold
: 如果规则优先级超过默认阈值,则跳过下条规则。import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Priority;
import org.jeasy.rules.annotation.Rule;
/**
* 被5整除的规则
* @author Nick Liu
* @date 2023/8/4
*/
@Rule
public class FizzRule {
@Condition
public boolean isFizz(@Fact("number") Integer number) {
return number % 5 == 0;
}
@Action
public void printFizz(@Fact("number") Integer number) throws Exception {
System.out.println("能被5整除的数为: " + number);
}
@Priority
public int getPriority() {
return 1;
}
}
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Priority;
import org.jeasy.rules.annotation.Rule;
/**
* 被7整除的规则
* @author Nick Liu
* @date 2023/8/4
*/
@Rule
public class BuzzRule {
@Condition
public boolean isBuzz(@Fact("number") Integer number) {
return number % 7 == 0;
}
@Action
public void printBuzz(@Fact("number") Integer number) throws Exception {
System.out.println("能被7整除的数为: " + number);
}
@Priority
public int getPriority() {
return 2;
}
}
import org.jeasy.rules.support.composite.UnitRuleGroup;
import java.util.Arrays;
/**
* 既能被5整除也能被7整除的规则
* @author Nick Liu
* @date 2023/8/4
*/
public class FizzBuzzRule extends UnitRuleGroup {
public FizzBuzzRule(Object... rules) {
Arrays.stream(rules).forEach(super::addRule);
}
@Override
public int getPriority() {
return 0;
}
}
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Priority;
import org.jeasy.rules.annotation.Rule;
/**
* 既不能被5整除也不能被7整除的规则
* @author Nick Liu
* @date 2023/8/4
*/
@Rule
public class NonFizzBuzzRule {
@Condition
public boolean isNotFizzOrBuzz(@Fact("number") Integer number) {
return number % 5 != 0 || number % 7 != 0;
}
@Action
public void printInput(@Fact("number") Integer number) throws Exception {
System.out.println("既不能被5整除也不能被7整除的数字: " + number);
}
@Priority
public int getPriority() {
return 3;
}
}
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.api.RulesEngineParameters;
import org.jeasy.rules.core.DefaultRulesEngine;
/**
* @author Nick Liu
* @date 2023/8/4
*/
public class FizzBuzzRulesLauncher {
public static void main(String[] args) {
// 如果第一条规则满足条件且行为执行成功后则跳过执行后面的规则
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
// 规则注册
Rules rules = new Rules();
rules.register(new FizzRule());
rules.register(new BuzzRule());
rules.register(new FizzBuzzRule(new FizzRule(), new BuzzRule()));
rules.register(new NonFizzBuzzRule());
// 触发规则
Facts facts = new Facts();
for (int count = 0; count < 100; count++) {
facts.put("number", count);
rulesEngine.fire(rules, facts);
}
}
}
上面我们设置了默认规则引擎的skipOnFirstAppliedRule
属性为true,优先级最高的规则是FizzBuzzRule
,这是一个组合规则,也就是说当满足该组合规则的条件,且规则行为执行成功后,就会跳过后面低优先级的所有规则。
简单来说,上面规则的执行顺序是FizzBuzzRule
→ FizzRule
→ BuzzRule
→ NonFizzBuzzRule
,语义就是先判断数字是否能被5和7同时整除,再判断是否能被5整除,再判断是否能被7整除,最后判断是否不能被5或7整除。
程序的运行结果如下:
能被5整除的数为: 0
能被7整除的数为: 0
既不能被5整除也不能被7整除的数字: 1
既不能被5整除也不能被7整除的数字: 2
既不能被5整除也不能被7整除的数字: 3
既不能被5整除也不能被7整除的数字: 4
能被5整除的数为: 5
既不能被5整除也不能被7整除的数字: 6
能被7整除的数为: 7
既不能被5整除也不能被7整除的数字: 8
既不能被5整除也不能被7整除的数字: 9
能被5整除的数为: 10
既不能被5整除也不能被7整除的数字: 11
既不能被5整除也不能被7整除的数字: 12
既不能被5整除也不能被7整除的数字: 13
能被7整除的数为: 14
能被5整除的数为: 15
既不能被5整除也不能被7整除的数字: 16
既不能被5整除也不能被7整除的数字: 17
既不能被5整除也不能被7整除的数字: 18
既不能被5整除也不能被7整除的数字: 19
能被5整除的数为: 20
能被7整除的数为: 21
既不能被5整除也不能被7整除的数字: 22
既不能被5整除也不能被7整除的数字: 23
既不能被5整除也不能被7整除的数字: 24
能被5整除的数为: 25
既不能被5整除也不能被7整除的数字: 26
既不能被5整除也不能被7整除的数字: 27
能被7整除的数为: 28
既不能被5整除也不能被7整除的数字: 29
能被5整除的数为: 30
既不能被5整除也不能被7整除的数字: 31
既不能被5整除也不能被7整除的数字: 32
既不能被5整除也不能被7整除的数字: 33
既不能被5整除也不能被7整除的数字: 34
能被5整除的数为: 35
能被7整除的数为: 35
既不能被5整除也不能被7整除的数字: 36
既不能被5整除也不能被7整除的数字: 37
既不能被5整除也不能被7整除的数字: 38
既不能被5整除也不能被7整除的数字: 39
能被5整除的数为: 40
既不能被5整除也不能被7整除的数字: 41
能被7整除的数为: 42
既不能被5整除也不能被7整除的数字: 43
既不能被5整除也不能被7整除的数字: 44
能被5整除的数为: 45
既不能被5整除也不能被7整除的数字: 46
既不能被5整除也不能被7整除的数字: 47
既不能被5整除也不能被7整除的数字: 48
能被7整除的数为: 49
能被5整除的数为: 50
既不能被5整除也不能被7整除的数字: 51
既不能被5整除也不能被7整除的数字: 52
既不能被5整除也不能被7整除的数字: 53
既不能被5整除也不能被7整除的数字: 54
能被5整除的数为: 55
能被7整除的数为: 56
既不能被5整除也不能被7整除的数字: 57
既不能被5整除也不能被7整除的数字: 58
既不能被5整除也不能被7整除的数字: 59
能被5整除的数为: 60
既不能被5整除也不能被7整除的数字: 61
既不能被5整除也不能被7整除的数字: 62
能被7整除的数为: 63
既不能被5整除也不能被7整除的数字: 64
能被5整除的数为: 65
既不能被5整除也不能被7整除的数字: 66
既不能被5整除也不能被7整除的数字: 67
既不能被5整除也不能被7整除的数字: 68
既不能被5整除也不能被7整除的数字: 69
能被5整除的数为: 70
能被7整除的数为: 70
既不能被5整除也不能被7整除的数字: 71
既不能被5整除也不能被7整除的数字: 72
既不能被5整除也不能被7整除的数字: 73
既不能被5整除也不能被7整除的数字: 74
能被5整除的数为: 75
既不能被5整除也不能被7整除的数字: 76
能被7整除的数为: 77
既不能被5整除也不能被7整除的数字: 78
既不能被5整除也不能被7整除的数字: 79
能被5整除的数为: 80
既不能被5整除也不能被7整除的数字: 81
既不能被5整除也不能被7整除的数字: 82
既不能被5整除也不能被7整除的数字: 83
能被7整除的数为: 84
能被5整除的数为: 85
既不能被5整除也不能被7整除的数字: 86
既不能被5整除也不能被7整除的数字: 87
既不能被5整除也不能被7整除的数字: 88
既不能被5整除也不能被7整除的数字: 89
能被5整除的数为: 90
能被7整除的数为: 91
既不能被5整除也不能被7整除的数字: 92
既不能被5整除也不能被7整除的数字: 93
既不能被5整除也不能被7整除的数字: 94
能被5整除的数为: 95
既不能被5整除也不能被7整除的数字: 96
既不能被5整除也不能被7整除的数字: 97
能被7整除的数为: 98
既不能被5整除也不能被7整除的数字: 99
public class FizzBuzzRulesLauncher {
public static void main(String[] args) {
// 如果第一条规则满足条件且行为执行成功后则跳过执行后面的规则
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstNonTriggeredRule(true);
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
// 规则注册
Rules rules = new Rules();
rules.register(new FizzRule());
rules.register(new BuzzRule());
rules.register(new FizzBuzzRule(new FizzRule(), new BuzzRule()));
rules.register(new NonFizzBuzzRule());
// 触发规则
Facts facts = new Facts();
for (int count = 0; count < 100; count++) {
facts.put("number", count);
rulesEngine.fire(rules, facts);
}
}
}
这里我们把规则引擎参数调整为skipOnFirstAppliedRule
,意味着当规则触发并且行为执行成功后就跳过下一条规则,执行结果如下:
能被5整除的数为: 0
能被7整除的数为: 0
能被5整除的数为: 0
能被7整除的数为: 0
能被5整除的数为: 35
能被7整除的数为: 35
能被5整除的数为: 35
能被7整除的数为: 35
能被5整除的数为: 70
能被7整除的数为: 70
能被5整除的数为: 70
能被7整除的数为: 70
**
* 被5整除的规则
* @author Nick Liu
* @date 2023/8/4
*/
@Rule
public class FizzRule {
@Condition
public boolean isFizz(@Fact("number") Integer number) {
return number % 5 == 0;
}
@Action
public void printFizz(@Fact("number") Integer number) throws Exception {
System.out.println("能被5整除的数为: " + number);
throw new RuntimeException("该数字能被5整除");
}
@Priority
public int getPriority() {
return 1;
}
}
这里我们修改了FizzRule
规则触发后的执行行为,当数字能被5整除时,执行行为后抛出异常。
public class FizzBuzzRulesLauncher {
public static void main(String[] args) {
// 如果第一条规则满足条件且行为执行成功后则跳过执行后面的规则
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstFailedRule(true);
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
// 规则注册
Rules rules = new Rules();
rules.register(new FizzRule());
rules.register(new BuzzRule());
rules.register(new FizzBuzzRule(new FizzRule(), new BuzzRule()));
rules.register(new NonFizzBuzzRule());
// 触发规则
Facts facts = new Facts();
for (int count = 0; count < 100; count++) {
facts.put("number", count);
rulesEngine.fire(rules, facts);
}
}
}
这里我们设置了规则引擎的参数skipOnFirstFailedRule
,也就是当判断规则是否触发时发生异常或者执行行为时发生异常后,就跳过下一条规则,程序运行结果如下:
能被5整除的数为: 0
既不能被5整除也不能被7整除的数字: 1
既不能被5整除也不能被7整除的数字: 2
既不能被5整除也不能被7整除的数字: 3
既不能被5整除也不能被7整除的数字: 4
能被5整除的数为: 5
既不能被5整除也不能被7整除的数字: 6
能被7整除的数为: 7
既不能被5整除也不能被7整除的数字: 7
既不能被5整除也不能被7整除的数字: 8
既不能被5整除也不能被7整除的数字: 9
能被5整除的数为: 10
既不能被5整除也不能被7整除的数字: 11
既不能被5整除也不能被7整除的数字: 12
既不能被5整除也不能被7整除的数字: 13
能被7整除的数为: 14
既不能被5整除也不能被7整除的数字: 14
能被5整除的数为: 15
既不能被5整除也不能被7整除的数字: 16
既不能被5整除也不能被7整除的数字: 17
既不能被5整除也不能被7整除的数字: 18
既不能被5整除也不能被7整除的数字: 19
能被5整除的数为: 20
能被7整除的数为: 21
既不能被5整除也不能被7整除的数字: 21
既不能被5整除也不能被7整除的数字: 22
既不能被5整除也不能被7整除的数字: 23
既不能被5整除也不能被7整除的数字: 24
能被5整除的数为: 25
既不能被5整除也不能被7整除的数字: 26
既不能被5整除也不能被7整除的数字: 27
能被7整除的数为: 28
既不能被5整除也不能被7整除的数字: 28
既不能被5整除也不能被7整除的数字: 29
能被5整除的数为: 30
既不能被5整除也不能被7整除的数字: 31
既不能被5整除也不能被7整除的数字: 32
既不能被5整除也不能被7整除的数字: 33
既不能被5整除也不能被7整除的数字: 34
能被5整除的数为: 35
既不能被5整除也不能被7整除的数字: 36
既不能被5整除也不能被7整除的数字: 37
既不能被5整除也不能被7整除的数字: 38
既不能被5整除也不能被7整除的数字: 39
能被5整除的数为: 40
既不能被5整除也不能被7整除的数字: 41
能被7整除的数为: 42
既不能被5整除也不能被7整除的数字: 42
既不能被5整除也不能被7整除的数字: 43
既不能被5整除也不能被7整除的数字: 44
能被5整除的数为: 45
既不能被5整除也不能被7整除的数字: 46
既不能被5整除也不能被7整除的数字: 47
既不能被5整除也不能被7整除的数字: 48
能被7整除的数为: 49
既不能被5整除也不能被7整除的数字: 49
能被5整除的数为: 50
既不能被5整除也不能被7整除的数字: 51
既不能被5整除也不能被7整除的数字: 52
既不能被5整除也不能被7整除的数字: 53
既不能被5整除也不能被7整除的数字: 54
能被5整除的数为: 55
能被7整除的数为: 56
既不能被5整除也不能被7整除的数字: 56
既不能被5整除也不能被7整除的数字: 57
既不能被5整除也不能被7整除的数字: 58
既不能被5整除也不能被7整除的数字: 59
能被5整除的数为: 60
既不能被5整除也不能被7整除的数字: 61
既不能被5整除也不能被7整除的数字: 62
能被7整除的数为: 63
既不能被5整除也不能被7整除的数字: 63
既不能被5整除也不能被7整除的数字: 64
能被5整除的数为: 65
既不能被5整除也不能被7整除的数字: 66
既不能被5整除也不能被7整除的数字: 67
既不能被5整除也不能被7整除的数字: 68
既不能被5整除也不能被7整除的数字: 69
能被5整除的数为: 70
既不能被5整除也不能被7整除的数字: 71
既不能被5整除也不能被7整除的数字: 72
既不能被5整除也不能被7整除的数字: 73
既不能被5整除也不能被7整除的数字: 74
能被5整除的数为: 75
既不能被5整除也不能被7整除的数字: 76
能被7整除的数为: 77
既不能被5整除也不能被7整除的数字: 77
既不能被5整除也不能被7整除的数字: 78
既不能被5整除也不能被7整除的数字: 79
能被5整除的数为: 80
既不能被5整除也不能被7整除的数字: 81
既不能被5整除也不能被7整除的数字: 82
既不能被5整除也不能被7整除的数字: 83
能被7整除的数为: 84
既不能被5整除也不能被7整除的数字: 84
能被5整除的数为: 85
既不能被5整除也不能被7整除的数字: 86
既不能被5整除也不能被7整除的数字: 87
既不能被5整除也不能被7整除的数字: 88
既不能被5整除也不能被7整除的数字: 89
能被5整除的数为: 90
能被7整除的数为: 91
既不能被5整除也不能被7整除的数字: 91
既不能被5整除也不能被7整除的数字: 92
既不能被5整除也不能被7整除的数字: 93
既不能被5整除也不能被7整除的数字: 94
能被5整除的数为: 95
既不能被5整除也不能被7整除的数字: 96
既不能被5整除也不能被7整除的数字: 97
能被7整除的数为: 98
既不能被5整除也不能被7整除的数字: 98
既不能被5整除也不能被7整除的数字: 99
可以看到,既能被5整除也能被7整除的数,如35和70只打印了一次。
Easy Rules
允许我们创建复杂的组合规则,所谓组合规则由多个单条规则组成,实际上就是组合设计模式的一种实现。
组合规则是一种抽象概念,因为组合规则可以通过不同的方式出发。Easy Rules
提供了三种组合规则的实现,这些实现可以在easy-rules-support
模块中可以找到,首先介绍一下3种组合规则:
该组合规则判断规则是否触发的代码如下,可以看到,所有的规则如果被触发,相应的行为都会被执行。
@Override
public boolean evaluate(Facts facts) {
if (!rules.isEmpty()) {
for (Rule rule : rules) {
if (!rule.evaluate(facts)) {
return false;
}
}
return true;
}
return false;
}
@Override
public void execute(Facts facts) throws Exception {
for (Rule rule : rules) {
rule.execute(facts);
}
}
从该组合规则的源代码可以看到,该规则组中只有第一条可以被触发的规则才会执行相应的规则行为。
@Override
public boolean evaluate(Facts facts) {
for (Rule rule : rules) {
if (rule.evaluate(facts)) {
selectedRule = rule;
return true;
}
}
return false;
}
@Override
public void execute(Facts facts) throws Exception {
if (selectedRule != null) {
selectedRule.execute(facts);
}
}
该组合规则的源代码如下,可以看到只有最高优先级的规则可以被触发,才会继续应用其它规则中的规则。
@Override
public boolean evaluate(Facts facts) {
successfulEvaluations = new HashSet<>();
conditionalRule = getRuleWithHighestPriority();
if (conditionalRule.evaluate(facts)) {
for (Rule rule : rules) {
if (rule != conditionalRule && rule.evaluate(facts)) {
successfulEvaluations.add(rule);
}
}
return true;
}
return false;
}
/**
* When a conditional rule group is executed, all rules that evaluated to true
* are performed in their natural order, but with the conditional rule
* (the one with the highest priority) first.
*
* @param facts The facts.
*
* @throws Exception thrown if an exception occurs during actions performing
*/
@Override
public void execute(Facts facts) throws Exception {
conditionalRule.execute(facts);
for (Rule rule : sort(successfulEvaluations)) {
rule.execute(facts);
}
}
private Rule getRuleWithHighestPriority() {
List<Rule> copy = sort(rules);
// make sure we only have one rule with the highest priority
Rule highest = copy.get(0);
if (copy.size() > 1 && copy.get(1).getPriority() == highest.getPriority()) {
throw new IllegalArgumentException("Only one rule can have highest priority");
}
return highest;
}
private List<Rule> sort(Set<Rule> rules) {
return new ArrayList<>(new TreeSet<>(rules));
}