Easy-Rules规则引擎多事实触发问题及Shop Tutorial扩展咨询
解决Easy-Rules中遍历多个事实进行规则评估的问题
我之前在扩展Easy-Rules的规则时也遇到过类似的需求——要一次性处理多个事实并完成规则评估,而不是仅针对单个实例。这里有两个实用的方案,你可以根据自己的场景选择:
方案1:将多个事实包装为集合类事实(推荐用于声明式规则)
如果习惯用表达式(比如你提到的condition: "people.isAdult() == false"这种写法),最直接的方式是把多个同类型的事实包装成一个集合对象,作为单个事实存入Facts容器,然后在规则条件和动作中通过这个集合对象遍历所有实例。
步骤示例:
定义集合包装类
先创建一个包含事实集合的类,封装遍历、过滤等逻辑,让规则表达式更简洁:import java.util.List; import java.util.stream.Collectors; public class PersonGroup { private List<Person> people; public PersonGroup(List<Person> people) { this.people = people; } // 判断集合中是否存在未成年人 public boolean hasMinors() { return people.stream().anyMatch(person -> !person.isAdult()); } // 获取所有未成年人 public List<Person> getMinors() { return people.stream() .filter(person -> !person.isAdult()) .collect(Collectors.toList()); } // getter public List<Person> getPeople() { return people; } }编写规则(表达式方式)
现在可以在规则的when和then中直接调用包装类的方法,实现多事实的遍历评估:import org.jeasy.rules.api.Rule; import org.jeasy.rules.core.RuleBuilder; Rule minorAlertRule = new RuleBuilder() .name("未成年人提醒规则") .description("检测并处理人群中的未成年人") .when("personGroup.hasMinors()") // 条件:集合中存在未成年人 .then("personGroup.getMinors().forEach(p -> System.out.println(p.getName() + \" 是未成年人,需要特殊处理\"))") // 动作:遍历处理所有符合条件的事实 .build();执行规则
将包装好的集合事实传入Facts,触发规则:import org.jeasy.rules.api.Facts; import org.jeasy.rules.api.Rules; import org.jeasy.rules.core.DefaultRulesEngine; public class Main { public static void main(String[] args) { // 创建多个Person事实 Person alice = new Person("Alice", 17); Person bob = new Person("Bob", 20); Person charlie = new Person("Charlie", 16); // 包装成集合事实 PersonGroup group = new PersonGroup(List.of(alice, bob, charlie)); Facts facts = new Facts(); facts.put("personGroup", group); Rules rules = new Rules(); rules.register(minorAlertRule); DefaultRulesEngine engine = new DefaultRulesEngine(); engine.fire(rules, facts); } }
方案2:自定义规则直接操作Facts容器(适合灵活逻辑)
如果需要更灵活的控制(比如动态获取不同类型的事实,或者复杂的遍历逻辑),可以自定义规则类,重写evaluate和execute方法,直接从Facts容器中取出所有事实进行遍历。
步骤示例:
自定义规则类
import org.jeasy.rules.api.Facts; import org.jeasy.rules.core.BasicRule; public class MinorCheckRule extends BasicRule { public MinorCheckRule() { super("未成年人检测规则", "遍历所有Person事实,处理未成年人"); } @Override public boolean evaluate(Facts facts) { // 遍历Facts中所有Person类型的事实,判断是否存在未成年人 return facts.values().stream() .filter(Person.class::isInstance) .map(Person.class::cast) .anyMatch(person -> !person.isAdult()); } @Override public void execute(Facts facts) throws Exception { // 遍历并处理所有符合条件的Person事实 facts.values().stream() .filter(Person.class::isInstance) .map(Person.class::cast) .filter(person -> !person.isAdult()) .forEach(person -> System.out.println("处理未成年人:" + person.getName())); } }执行规则
直接将多个Person事实存入Facts,注册自定义规则并触发:import org.jeasy.rules.api.Facts; import org.jeasy.rules.api.Rules; import org.jeasy.rules.core.DefaultRulesEngine; public class Main { public static void main(String[] args) { Facts facts = new Facts(); facts.put("alice", new Person("Alice", 17)); facts.put("bob", new Person("Bob", 20)); facts.put("charlie", new Person("Charlie", 16)); Rules rules = new Rules(); rules.register(new MinorCheckRule()); DefaultRulesEngine engine = new DefaultRulesEngine(); engine.fire(rules, facts); } }
方案对比
- 方案1:更符合Easy-Rules的声明式风格,规则逻辑清晰,适合用表达式或配置文件(YAML/JSON)定义规则的场景。
- 方案2:灵活性更高,适合需要动态处理不同类型事实、或者逻辑复杂无法用简单表达式实现的场景,但需要编写Java代码实现规则逻辑。
内容的提问来源于stack exchange,提问作者E.Bülbül
相关产品推荐
相关产品推荐

