Easy-Rules引擎

Easy-Rules规则引擎

使用情景:

​ 当代码需要多次进行if/else硬编码的时候,会导致代码的可读性大大降低,后期维护的成本增高。所以引入规则引擎,easy-rules是一个简单但是功能强大的规则引擎,提供了以下特性:

  • 轻量级框架和易学习的API
  • 基于POJO 的开发
  • 支持从原始规则进行规则的组合
  • 支持表达式(MVEL,SPEL和JEXL)定义规则

主要的接口:

​ Rules(规则接口),Facts(事实接口),RulesEngine(规则引擎接口)

快速开始

下面使用POJO 的方式进行示例

想了MVEL等方式定义的实例转到: https://segmentfault.com/a/1190000022939252

官方文档的GitHub地址:https://github.com/j-easy/easy-rules/wiki/defining-rules

1. 导入依赖

<!-- 导入此依赖只能使用注解形式进行规则创建及添加 -->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-core</artifactId>
    <version>4.1.0</version>
</dependency>
<!--使用mvel和spel进行配置的时候需要导入的依赖-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-mvel</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-spel</artifactId>
    <version>3.4.0</version>
</dependency>

2. 规则编写

**规则一:**能被3整除的数

@Rule(name = "被3整除", description = "number如果被3整除,打印:number is three", priority = 1)
public class ThreeRule {
    /**
     * Condition:条件判断注解:如果return true, 执行Action
     * @param number
     */
    @Condition
    public boolean isThree(@Fact("number") int number) {
        return number % 3 == 0;
    }
 // Action 执行方法注解
    @Action
    public void threeAction(@Fact("number") int number) {
        System.out.println(number + " is three");
    }
}

**规则二:**能被8整除

@Rule(name = "被8整除", priority = 1)
public class EightRule {
    @Condition
    public boolean isEight(@Fact("number") int number) {
        return number % 8 == 0;
    }
    @Action
    public void eightAction(@Fact("number") int number) {
        System.out.println(number + " is eight");
    }
}

**组合规则:**既能被8整除又能被3整除

@Rule(name = "被3和8同时整除", description = "这是一个组合规则", priority = 0)
public class ThreeEightRuleUnitGroup extends UnitRuleGroup {
    // 传入其他规则进行组合
    public ThreeEightRuleUnitGroup(Object... rules) {
        for (Object rule : rules) {
            addRule(rule);
        }
    }
}

3. 初始化规则引擎,注册规则

class RulesTest {
    @Test
    public void test() {
        // 初始化规则引擎
        RulesEngineParameters parameters = new RulesEngineParameters()
                .skipOnFirstAppliedRule(true);
        DefaultRulesEngine engine = new DefaultRulesEngine(parameters);
        // 注册规则进入引擎
        Rules rules = new Rules();
        rules.register(new ThreeRule());
        rules.register(new EightRule());
        rules.register(new ThreeEightRuleUnitGroup(new ThreeRule(), new EightRule()));
        Facts facts = new Facts();
        facts.put("number", 24);
        // 开始判断
        engine.fire(rules, facts);
    }
}

Rule(规则)

可以把规则理解为if语句和满足条件后的执行体,当**@Condition注解的方法返回真的时候则执行@Action注解的**方法

1. Annotation

  • @Rule:写在Rule类上,标识这是一个规则,可选参数name(规则命名空间的唯一规则名称),description(描述规则的作用),priority(设置规则的优先级,值越优先级越高,不设置默认为**-1**,可以通过这个设置默认的响应规则
  • @Condition: 写在方法上作为判断条件,为真的时候返回True并执行**@Action方法,返回False**的时候则直接跳过

2. Rule定义

使用注解定义Rule

@Rule(name = "my rule", description = "my rule description", priority = 1)
public class MyRule {

    @Condition
    public boolean when(@Fact("fact") fact) {
        // 规则条件
        return true;
    }
    @Action(order = 1)
    public void then(Facts facts) throws Exception {
        // 规则为true时的操作1
    }

    @Action(order = 2)
    public void finally() throws Exception {
        // 规则为true时的操作2
    }
}

使用RuleBuilder定义规则

Rule rule = new RuleBuilder()
                .name("myRule")
                .description("myRuleDescription")
                .priority(3)
                .when(condition) // condition是Condition接口的实例对象
                .then(action1) // action是Aciton接口的实例对象
                .then(action2)
                .build();

3. 组合规则

可以用前面基础的Rule规则自由组合一个更加复杂的规则

组合规则的分类

  • UnitRuleGroup: 是作为一个单元使用的组合规则,要么所有规则都应用,要么都不用
  • ActivationRuleGroup:激活规则组中触发第一个使用规则并忽略其他规则。规则首先按照在组中的自然顺序(默认情况下优先级)进行排序
  • ConditionalRuleGroup: 条件规则组将具有最高优先级的规则作为条件,如果具有最高优先级的规则的计算结果为true,那么将触发其余的规则

使用UnitRuleGroup定义

// 从两个原始规则创建组合规则
UnitRuleGroup myUnitRuleGroup =  new ThreeEightRuleUnitGroup("ThreeRule", "EightRule");

// 像常规规则一样注册组合规则
Rules rules = new Rules();
rules.register(myUnitRuleGroup);
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, someFacts);

// 定义的组合规则类
@Rule(name = "被3和8同时整除", description = "这是一个组合规则")
public class ThreeEightRuleUnitGroup extends UnitRuleGroup {

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

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

}

Facts(事实)

事实在我的理解就是为Rule规则提供类似反射一样的判断情况,如果没有Fact那么Rule的判断和普通的if/else没有区别,还是一种硬编码的判断。当加入了Facts的时候,我们可以自定义丰富的Fact事实去进行多元的判断,修改判断逻辑的时候可以不修改Rule就能实现

1. fact的组成

public class Fact<T> {
   private final String name;
   private final T value;  
}

fact必须拥有两个值,一个名称和一个值。两个都不能为null,name代表的是事实的命名空间,会根据name发送给Rule中Fact相同的进行判断。value则可以携带判断信息和执行的方法。

**注:**可以自定义一个类,用来将判断的条件和判读后执行的方法都集成到里面去,减少代码的冗余

代码案例如下:

Facts facts = new Facts();
facts.put("wheather", "rain");  // 事实会被注入到对应的Rule中去

@Rule
class WeatherRule {
    @Condition
    public boolean itRains(@Fact("wheather") String wheather) {
        return "rain".equals(wheather);
    }
    @Action
    public void takeAnUmbrella(@Fact("wheather") String wheather) {
        System.out.println("It rains, take an umbrella!");
    }

}

注:

  • 如果条件方法中缺少注入的事实,引擎将记录一个警告,并认为条件被计算为false
  • 如果动作方法中缺少注入的事实,则不会执行该动作,并且抛出org.jeasy.rules.core.NoSuchFactException异常。

RulesEngine

进行规则和事实进行判断的启动入口

1. RulesEngine类型:

  • DefaultRulesEngine:根据规则的自然顺序(默认为优先级)应用规则。
  • InferenceRulesEngine:在已知的事实上不断地应用规则,直到没有更多的规则可用

2. 规则引擎的参数

参数类型默认值
rulePriorityThresholdintMaxInt
skipOnFirstAppliedRulebooleanfalse
rulePriorityThresholdintfalse
skipOnFirstFailedRulebooleanfalse
skipOnFirstNonTriggeredRulebooleanfalse
  • skipOnFirstAppliedRule:当一个规则成功应用时,跳过余下的规则。
  • skipOnFirstFailedRule:当一个规则失败时,跳过余下的规则。
  • skipOnFirstNonTriggeredRule:当一个规则未触发时,跳过余下的规则。
  • rulePriorityThreshold:当优先级超过指定的阈值时,跳过余下的规则。

3. 规则引擎的创建

// 1.先设置规则引擎的参数RulesEngineParameters parameters = new RulesEngineParameters()
RulesEngineParameters parameters = new RulesEngineParameters()
        .rulePriorityThreshold(10)
        .skipOnFirstAppliedRule(true)
        .skipOnFirstFailedRule(true)
        .skipOnFirstNonTriggeredRule(true);

// 2. 将引擎参数添加到规则引擎中去,进行规则引擎的初始化
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

Listener

1. 定义规则监听器

如果想对规则进行监听,需要自定义个一个类去实现这个接口然后注册到规则引擎当中去

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.
     */
    default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) { }

    /**
     * Triggered on condition evaluation error due to any runtime exception.
     */
    default void onEvaluationError(Rule rule, Facts facts, Exception exception) { }

    /**
     * Triggered before the execution of a rule.
     */
    default void beforeExecute(Rule rule, Facts facts) { }

    /**
     * Triggered after a rule has been executed successfully.
     */
    default void onSuccess(Rule rule, Facts facts) { }

    /**
     * Triggered after a rule has failed.
     */
    default void onFailure(Rule rule, Facts facts, Exception exception) { }

}

2. 定义规则引擎监听器

public interface RulesEngineListener {
    /**
     * @param rules to fire
     * @param facts present before firing rules
     */
    default void beforeEvaluate(Rules rules, Facts facts) { }

    /**
     * @param rules fired
     * @param facts present after firing rules
     */
    default void afterExecute(Rules rules, Facts facts) { }
}

3. 使用例子

创建一个规则监听器的例子,规则引擎的类似

public class MyRuleListener implements RuleListener {
    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        System.out.println(rule.getName() + "is going to evaluate !!");
        return true;
    }

    @Override
    public void beforeExecute(Rule rule, Facts facts) {
        System.out.println(rule.getName() + "is going to execute !");
    }
}

// 注册到规则引擎中去  
DefaultRulesEngine engine = new DefaultRulesEngine();
engine.registerRuleListener(new MyRuleListener());	
  • 32
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,以下是 easy-rules-core 版本为 3.2.0 的使用示例代码: 首先,我们需要定义一个规则: ```java public class AgeRule extends BasicRule { private int age; public AgeRule(int priority, int age) { super("AgeRule", "Check if age is greater than 18", priority); this.age = age; } @Override public boolean evaluate(Facts facts) { int age = facts.get("age"); return age > this.age; } @Override public void execute(Facts facts) throws Exception { System.out.println("Age is greater than 18"); } } ``` 在这个规则中,我们检查一个名为 "age" 的事实是否大于18。如果是,我们将打印一条消息。 接下来,我们需要创建一个规则引擎,并将规则添加到其中: ```java Rules rules = new Rules(); rules.register(new AgeRule(1, 18)); RulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.fire(rules, new Facts()); ``` 在这个例子中,我们创建了一个规则引擎并将 AgeRule 添加到其中。然后,我们使用一个空的 Facts 对象来启动规则引擎。 当我们运行这段代码时,因为我们提供的 "age" 事实的值为0,所以我们不会看到任何输出。 我们可以通过将 "age" 事实的值设置为20来测试规则: ```java Rules rules = new Rules(); rules.register(new AgeRule(1, 18)); Facts facts = new Facts(); facts.put("age", 20); RulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.fire(rules, facts); ``` 当我们运行这段代码时,我们将在控制台上看到 "Age is greater than 18" 的消息。 希望这个示例能够帮助你理解如何使用 easy-rules-core 版本为 3.2.0。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值