Easy Rules rule engine (2-Details)

1. Preface

In the Easy Rules rule engine (1-Basics), we have briefly introduced Easy Rulesthe use examples of the rule engine. In this section, we will explain in detail the relevant parameter configuration examples and combination rules of the rule engine.


2. Rule engine parameter configuration example

Easy RulesThe rule engine supports the following parameter configurations:

parameter name Parameter Type required default value
rulePriorityThreshold int no Integer.MAX_VALUE
skipOnFirstAppliedRule boolean no false
skipOnFirstFailedRule boolean no false
skipOnFirstNonTriggeredRule boolean no false
  • skipOnFirstAppliedRule: Whether to skip the next rule when the rule is triggered and the behavior is successfully executed.
  • skipOnFirstFailedRule: When judging whether a rule is triggered, an exception is thrown or whether the next rule is skipped if an exception is thrown after the trigger is successful but the behavior is executed.
  • skipOnFirstNonTriggeredRule : Whether to skip the next rule when the rule is not triggered.
  • rulePriorityThreshold : If the rule priority exceeds the default threshold, the next rule is skipped.

1、skipOnFirstAppliedRules example

(1) FizzRule

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

}

(2) BuzzRule

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

(3) FizzBuzzRule

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

(4) NonFizzBuzzRule

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

}

(5) FizzBuzzRulesLauncher

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

Above we set skipOnFirstAppliedRulethe attribute of the default rule engine to true . The rule with the highest priority is FizzBuzzRulethat this is a combination rule. That is to say, when the conditions of the combination rule are met and the rule behavior is executed successfully, the following low-priority rules will be skipped. All level rules.

To put it simply, the execution order of the above rules is FizzBuzzRuleFizzRuleBuzzRuleNonFizzBuzzRule. The semantics is to first determine whether the number is divisible by 5 and 7 at the same time, then determine whether it is divisible by 5, then determine whether it is divisible by 7, and finally determine whether it is not divisible by 5. Or divisible by 7.

The results of running the program are as follows:

能被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

2、skipOnFirstNonTriggeredRule example

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

Here we adjust the rule engine parameters to skipOnFirstNonTriggeredRule, which means that when the highest priority rule is not triggered, the following rules will be skipped. The execution results are as follows:

能被5整除的数为: 0
能被7整除的数为: 0
能被5整除的数为: 0
能被7整除的数为: 0
能被5整除的数为: 35
能被7整除的数为: 35
能被5整除的数为: 35
能被7整除的数为: 35
能被5整除的数为: 70
能被7整除的数为: 70
能被5整除的数为: 70
能被7整除的数为: 70

3、skipOnFirstFailedRule example

**
 *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;
	}

}

Here we modify FizzRulethe execution behavior after the rule is triggered. When the number is divisible by 5, an exception is thrown after the execution behavior.

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

Here we set the parameters of the rule engine skipOnFirstFailedRule, that is, when an exception occurs when judging whether a rule is triggered or an exception occurs when executing a behavior, the next rule will be skipped. The program running results are as follows:

能被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

As you can see, numbers that are divisible by both 5 and 7, such as 35 and 70 , are only printed once.


3. Combination rules

Easy RulesAllows us to create complex combination rules . The so-called combination rules are composed of multiple single rules, which is actually an implementation of the combination design pattern.

Combining rules is an abstract concept because combining rules can be approached in different ways. Easy RulesImplementations of three combination rules are provided. These implementations can be easy-rules-supportfound in the module. First, the three combination rules are introduced:

  • UnitRuleGroup: Unit rule group, all rules in it will be applied.
  • ActivationRuleGroup: When activating a rule group, only the first applicable rule will be triggered. All rules in the rule group are sorted by priority by default.
  • ConditionalRuleGroup: Conditional rule, if the highest priority rule can be triggered, then the rules in other rule groups will be triggered.

1. UnitRuleGroup combination rules

The code for this combination rule to determine whether the rule is triggered is as follows. You can see that if all the rules are triggered, the corresponding behavior will be executed.

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

2. ActivationRuleGroup combination rules

From the source code of this combination rule, we can see that only the first rule in the rule group that can be triggered will execute the corresponding rule behavior.

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

3. ConditionalRuleGroup combination rules

The source code of this combination rule is as follows. You can see that only the highest priority rule can be triggered, and then the rules in other rules will continue to be applied.

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

Insert image description here

Guess you like

Origin blog.csdn.net/lingbomanbu_lyl/article/details/132222367