如何仅向Drools决策表的ACTION区域传递对象?
我明白你现在的困扰——用Drools决策表时,想通过只读输入对象和可变输出对象分离规则的输入输出,但目前只能用虚拟条件列的hack来让ACTION区识别输出对象,确实不够优雅。下面我给你几个更规范的解决方案,帮你摆脱这个窘境:
方案1:在CONDITION区域明确匹配输出对象
这是最符合Drools规则设计思路的方式,你不需要虚拟条件列,只需要在CONDITION区添加一行来匹配工作内存中的输出对象即可:
假设你的决策表结构如下(简化版):
| CONDITION | ACTION |
|---|---|
| $input: RuleInput() | // 基于$input的规则逻辑 |
| $output: RuleOutput() | $output.setPrice(...) |
这样在CONDITION区的第二行,我们明确声明并匹配了工作内存中的RuleOutput对象,给它起了别名$output,之后在ACTION区就能直接使用这个变量了,完全不需要虚拟条件。
你之前尝试把输入和输出放在同一单元格报错,是因为Drools决策表的CONDITION单元格中,每个对象模式需要单独一行(或格式正确的分号分隔),分开两行声明是最清晰且不易出错的方式。
方案2:使用全局变量(Global)传递输出对象
如果你的输出对象是全局共享的(比如所有规则都要修改同一个输出对象),可以把它设置为Drools的全局变量,这样不需要在CONDITION区匹配,直接在ACTION区引用:
第一步:修改Kotlin代码,设置全局变量
private fun calculatePrice(input: RuleInput, path: String): RuleOutput { val kieRepository: KieRepository = kieServices.repository kieRepository.addKieModule { kieRepository.defaultReleaseId } val kb: KieBuilder = kieServices.newKieBuilder(prepareFileSystem(kieServices, path)) kb.buildAll() val kieModule: KieModule = kb.kieModule val kieContainer: KieContainer = kieServices.newKieContainer(kieModule.releaseId) val output = RuleOutput() kieContainer.newKieSession().apply { setGlobal("ruleOutput", output) // 将输出对象设为全局变量 insert(input) fireAllRules() } return output }
第二步:在决策表中声明并使用全局变量
你需要在决策表的RuleSet区域(或者对应的DRL文件开头)添加全局变量的声明:
global com.yourpackage.RuleOutput ruleOutput;
之后在ACTION区就可以直接使用这个全局变量了:
ruleOutput.setDiscount(0.8)
为什么你之前的方式会报错?
你提到在ACTION区直接用$output会提示无法解析,核心原因是:Drools规则中的变量必须先在CONDITION区通过模式匹配声明,或者通过全局变量定义,否则规则引擎无法识别这个变量的存在。而你之前的虚拟条件列本质上是通过一个恒真的条件(比如1==1)偷偷声明了$output变量,虽然能工作,但属于不规范的hack。
推荐方案
如果你的输出对象是和输入对象一一对应的(每个输入对应一个输出),优先选择方案1,因为它更符合Drools的规则匹配逻辑,代码可读性更高;如果是多个规则共享同一个全局输出对象,方案2会更简洁。
内容的提问来源于stack exchange,提问作者ar811

