如何在Drools规则中存储、传递各规则生成的中间计算值
Drools跨规则传递中间值解决方案
你的规则拆分思路完全符合Drools的最佳实践,将复杂计算拆分为多个独立的中间值规则,反而能大幅提升规则的可读性和可维护性,不存在思路问题。
Drools本身没有原生提供规则集内私有全局变量的能力,工业界通用的跨规则数据传递方案有以下两种:
方案1:自定义评分上下文Fact(最推荐)
这是最符合Drools设计范式的实现方式,完全满足你对「规则集内可用、可设置初始值」的需求:
- 先定义一个专门存储评分过程数据的上下文类,将中间值、最终得分都作为类属性,直接在类中配置默认初始值
- 触发规则前,将
Person业务对象、上下文类对象同时插入Drools工作内存 - 所有中间值计算规则直接修改上下文对象的属性,最终得分规则直接读取上下文对象中已计算完成的中间值即可
代码示例
上下文类定义
import java.math.BigDecimal; // 评分上下文Fact,仅在当前规则会话生命周期内有效 public class ScoreContext { // 直接设置初始值 private BigDecimal intrmdt1 = BigDecimal.ZERO; private BigDecimal intrmdt2 = BigDecimal.ZERO; private BigDecimal intrmdt3 = BigDecimal.ZERO; private BigDecimal finalScore = BigDecimal.ZERO; // 省略所有属性的getter、setter方法 }
DRL规则示例
import com.yourpackage.Person import com.yourpackage.ScoreContext import java.math.BigDecimal // 中间值1计算规则,优先级100(数值越大执行优先级越高) rule "calcIntrmdt1" salience 100 when $person: Person() $ctx: ScoreContext() then // 此处编写你的中间值1计算逻辑 BigDecimal calcResult = $person.getAge() == null ? BigDecimal.ZERO : BigDecimal.valueOf($person.getAge() * 2); $ctx.setIntrmdt1(calcResult); // 若有其他规则依赖该中间值触发,可加update($ctx)通知工作内存数据变更 end // 中间值2计算规则,优先级99 rule "calcIntrmdt2" salience 99 when $person: Person() $ctx: ScoreContext() then // 中间值2计算逻辑 end // 中间值3计算规则,优先级98 rule "calcIntrmdt3" salience 98 when $person: Person() $ctx: ScoreContext() then // 中间值3计算逻辑 end // 最终得分计算规则,优先级最低,确保所有中间值计算完成后执行 rule "calcFinalScore" salience 0 when $ctx: ScoreContext() then BigDecimal total = $ctx.getIntrmdt1() .add($ctx.getIntrmdt2()) .add($ctx.getIntrmdt3()); $ctx.setFinalScore(total); end
规则调用逻辑
// 构造业务对象和上下文对象 Person person = getPersonFromBiz(); ScoreContext scoreCtx = new ScoreContext(); // 插入工作内存 KieSession ksession = kieContainer.newKieSession(); ksession.insert(person); ksession.insert(scoreCtx); // 执行规则 ksession.fireAllRules(); ksession.dispose(); // 直接从上下文对象获取最终得分 BigDecimal finalScore = scoreCtx.getFinalScore();
方案2:全局变量(仅适用固定执行顺序场景)
如果不想额外定义上下文类,也可以使用Drools全局变量实现,但是注意:全局变量的变更不会被Drools推理引擎感知,仅适合你已经明确通过优先级控制了执行顺序的场景:
- 在DRL顶部声明全局变量,通常用
Map类型存储多个中间值 - 触发规则前给会话设置全局变量,并写入初始值
- 所有规则直接读写该全局变量即可
代码示例
DRL全局变量声明
global java.util.Map scoreCache;
会话初始化时设置初始值
Map<String, Object> scoreCache = new HashMap<>(); scoreCache.put("intrmdt1", 0); scoreCache.put("intrmdt2", 0); scoreCache.put("intrmdt3", 0); ksession.setGlobal("scoreCache", scoreCache);
执行顺序控制建议
- 简单场景直接用
salience属性控制优先级即可,中间值规则优先级设置高于最终得分规则 - 规则数量多的复杂场景建议用
agenda-group分组,将所有中间值计算规则归为同一分组,最终得分规则归为另一分组,代码中手动控制分组的触发顺序,逻辑更清晰。
内容的提问来源于stack exchange,提问作者Sebastian Lore
相关产品推荐
相关产品推荐

