4. 规则表达与语法:DRL规则语言、IF-THEN结构、规则优先级与冲突解决

好,咱们今天聊聊规则引擎里最核心的东西——规则怎么写,怎么排优先级,怎么解决冲突。

说实话,我见过不少团队,规则写得跟天书似的,最后维护成本高得吓人。其实规则表达这件事,说白了就是两件事:怎么把业务逻辑说清楚,以及多条规则打架时听谁的

4.1 DRL规则语言:规则引擎的“普通话”

DRL(Drools Rule Language)是Drools规则引擎的原生语言。我个人习惯把它叫做“规则界的普通话”——虽然各家引擎都有自己的方言,但DRL的语法结构基本是通用的。

一个典型的DRL规则长这样:

rule "逾期金额大于1000且信用分低于600"
    when
        $loan: Loan(overdueAmount > 1000)
        $user: User(creditScore < 600)
    then
        $loan.setRiskLevel("高");
        System.out.println("触发高风险规则");
end

嗯,这里要注意几个关键点:

  • rule:规则的名字,最好起得有意义,别叫“rule1”、“rule2”这种
  • when:条件部分,说白了就是“如果”
  • then:动作部分,就是“那么”
  • $loan:变量绑定,方便后面引用
我的小习惯:规则名字我一般用“业务场景_触发条件_动作”的格式,比如“风控_逾期高_拒绝”。这样看名字就知道规则在干嘛,不用点进去看代码。

4.2 IF-THEN结构:规则引擎的“骨架”

其实不管什么规则引擎,底层都是IF-THEN结构。你想想看,业务规则本质上就是“如果发生了A,那么就执行B”。

我见过最坑的写法是什么?把IF-THEN嵌套了七八层。比如:

// 千万别这么写!
if (条件A) {
    if (条件B) {
        if (条件C) {
            // 动作
        }
    }
}

这种写法,维护起来简直要命。我在项目中遇到过,一个规则改了三个月,最后发现是嵌套逻辑搞错了。

正确的做法是扁平化

rule "规则1"
    when
        条件A
        条件B
        条件C
    then
        // 动作
end

DRL里的when部分,多个条件默认是“且”的关系。如果你想用“或”,可以用||或者or关键字。

核心原则:一条规则只做一件事。如果逻辑太复杂,拆成多条规则,让引擎帮你处理优先级和冲突。

4.3 规则优先级:谁先谁后?

规则多了,总有个先后顺序。DRL里用salience来控制优先级。数值越大,优先级越高。

rule "高优先级规则"
    salience 100
    when
        // 条件
    then
        // 动作
end

rule "低优先级规则"
    salience 10
    when
        // 条件
    then
        // 动作
end

我曾经犯过一个错:给所有规则都设了salience,但没注意数值范围。结果后来加规则时,发现优先级乱套了。从那以后,我定了个规矩:salience用100的倍数,留出中间值给后续扩展。

优先级级别 salience值 适用场景
最高 1000 黑名单、强制拒绝
500 高风险规则
100 常规规则
10 默认规则、兜底规则

4.4 冲突解决:规则打架怎么办?

规则冲突,说白了就是两条规则都满足条件,但结果互相矛盾。比如一条说“通过”,另一条说“拒绝”。

DRL里解决冲突有几种策略:

  • salience优先:谁优先级高听谁的
  • 加载顺序:如果salience相同,先加载的先执行
  • 自定义冲突策略:可以自己写逻辑
避坑指南:千万不要依赖加载顺序!不同环境(开发、测试、生产)的加载顺序可能不一样。我曾经在测试环境跑得好好的,上了生产就出问题,查了半天发现是规则文件加载顺序变了。从那以后,我强制要求所有规则必须显式设置salience。

还有一种常见的冲突场景:规则覆盖。比如规则A说“年龄大于30通过”,规则B说“年龄大于40拒绝”。那35岁的人怎么办?

我的建议是:明确规则的边界。要么用更精确的条件,要么用优先级来区分。比如:

rule "年龄30-40通过"
    salience 100
    when
        $user: User(age > 30, age <= 40)
    then
        $user.setResult("通过");
end

rule "年龄大于40拒绝"
    salience 200
    when
        $user: User(age > 40)
    then
        $user.setResult("拒绝");
end

这样,35岁的人只匹配第一条规则,45岁的人只匹配第二条规则,完美避开冲突。

4.5 知识体系总览

下面这张图,是我自己总结的规则表达与语法的核心结构。你把它记在脑子里,写规则时就不会跑偏。

规则表达与语法核心结构 规则引擎语法 DRL规则语言 IF-THEN结构 优先级与冲突解决 rule / when / then / end 变量绑定:$对象.属性 条件扁平化,避免嵌套 一条规则只做一件事 salience数值控制优先级 显式设置,避免依赖加载顺序 核心原则:清晰、扁平、显式、单一职责

嗯,这张图基本把这一章的核心内容都串起来了。你写规则时,就照着这个结构来,不会出大问题。

最后说一句:规则引擎这东西,语法本身不难,难的是怎么组织好规则。我见过太多人把规则写得又臭又长,最后自己都看不懂。记住:好的规则,读起来像业务文档

公众号:蓝海资料掘金营,微信deep3321