本文将介绍drools7中规则的继承。官方文档对应章节标题为 “conditional named consequences”,这里之所以称之为“继承”是因为在编写rule时会用到“extends ”,这样翻译其实是有一定问题的,翻译为扩展更准确些。本文将结合官方文档中的案例进行阐述。
通常每个rule只会写一个then,当多个rule仅其中某一个条件不一致时,就会写很多重复的代码,举个例子:假设某购物中心为吸引客户,决定给60岁以上的客户提供10%的优惠,并且给予免费停车的权益。
entity:
//客户
public class Customer {
private Integer id;
private int age;
private String level;
private double discount;
}
//客户的车辆
public class Car {
private Integer id;
private Integer owner;//车主id,即customer的id
private boolean freeParking;
}
kiesession初始化和插入数据源:
KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer =kieServices.getKieClasspathContainer();
KieBase kiebase = kieContainer.getKieBase();
KieSession kieSession = kieContainer.newKieSession();
Customer customer = new Customer(1,61,0.0,"golden");
Car car =new Car(1,customer.getId(),false);
kieSession.insert(customer);
kieSession.insert(car);
kieSession.fireAllRules();
kieSession.dispose();
1. 规则传统写法:
//方案1
rule "Give 10% discount to customers older than 60_f"
when
$c:Customer(age > 60)
then
System.out.println("方案1:Give 10% discount!");
end
rule "Give free parking to customers older than 60_f"
when
$c:Customer(age > 60)
Car(owner == $c.id)
then
System.out.println("方案1:Free parking!");
end
执行结果:
方案1:Give 10% discount!
方案1:Free parking!
分析:好的,没问题,但是 $c:Customer(age > 60)这一句每个rule里都需要写一遍,所以代码重复、维护起来较麻烦。
2. 再看,使用extends的写法:
//方案2
rule "Give 10% discount to customers older than 60_s"
when
$c:Customer(age > 60)
then
System.out.println("方案2:Give 10% discount!");
end
rule "Give free parking to customers older than 60_s"
extends "Give 10% discount to customers older than 60_s"
when
Car(owner == $c.id)
then
System.out.println("方案2:Free parking!");
end
执行结果:
方案2:Give 10% discount!
方案2:Free parking!
分析:好的,也没问题,第二个rule使用extends基于第一个rule扩展了规则条目并配置了更多的数据处理方式,第二个规则依赖于第一规则中的规则条目,第一个规则改动conditions时第二个规则也会同时生效,维护难度的问题得到解决,但是写法上似乎还是比较繁琐。
3. 继续优化写法:
//方案3
rule "Give 10% discount and free parking to customers older than 60_f"
when
$c:Customer(age > 60)
do[giveDiscount]
Car(owner == $c.id)
then
System.out.println("方案3:Free parking!");
then[giveDiscount]
System.out.println("方案3:Give 10% discount!");
end
执行结果:
方案3:Give 10% discount!
方案3:Free parking!
分析:写法相当清秀,上述规则包含两个条件,触发了默认规则处理(第一个then),另外通过do关键字又触了“giveDiscount”规则处理代码,do的作用是当do之前的规则条目都匹配的时候就会触发do后边中括号里绑定的标签所对应的的then。其本质就是将方案1中的两个规则合并为一个规则写,运行过程可以按方案1解释。
4. 拓展案例
写这篇文章的初衷其实是因为在技术群中看到有人问到一个问题,当规则条件中存在或的关系时,想知道具体是因为符合了哪个条件,那这里其实可以理解为对于符合规则的数据除了共有的处理以外,还有各自独特的输出,即规则规定数据符合条件A或条件B时,均输出成功信息,同时符合A条件的另外输出A,符合B条件的另外外输出B。通常的写法是拆分为两个rule去写,但是额外的输出又不是那么重要,拆分的话开发成本就会增加。这时候用对案例3进行一下升级改造就可以完美解决。
假设上述的购物中心对活动决策做了进一步调整,针对60岁以上的客户,“golden”级别的给予20%的优惠,“level”级别的给予10%的优惠,其他级别的优惠5%;给予免费停车的权益。
规则如下:
//方案4
rule "Give 10% discount and free parking to customers older than 60_s"
when
$c:Customer(age > 60,$l:level)
if ($l == "golden") do[golden]
else if ($l == 'silver') do[silver]
else do[other]
Car(owner == $c.id)
then
System.out.println("方案4:Free parking!");
then[golden]
System.out.println("方案4:golden, Give 20% discount!");
then[silver]
System.out.println("方案4:silver, Give 10% discount!");
then[other]
System.out.println("方案4:other, Give 5% discount!");
end
运行结果:
方案4:golden, Give 20% discount!
方案4:Free parking!
分析:这里又有了if/else的嵌套if、else if、else之间属于互斥关系,当if之前的条件匹配并且if中的条件时,就会触发其后do所绑定标签所对应的的then。
另外处理do关键字,还有break关键字,当触发break所绑定的标签时,其后的所有条件均不会在进行匹配,此时也不会再触发默认then了(即第一个then),示例如下:
rule "Give 10% discount and free parking to customers older than 60_s"
when
$c:Customer(age > 60,$l:level)
if ($l == "golden") break[golden] //触发break绑定的标签[golden]所对应的的then
else if ($l == 'silver') do[silver]
else do[other]
Car(owner == $c.id)//因为break被触发,这里不会执行条件匹配
then
System.out.println("方案4:Free parking!");//因为break,这里也不会被执行
then[golden]
System.out.println("方案4:golden, Give 20% discount!");//触发
then[silver]
System.out.println("方案4:silver, Give 10% discount!");
then[other]
System.out.println("方案4:other, Give 5% discount!");
end
执行结果:
方案4:golden, Give 20% discount!
规则就是让我们带着锁链跳舞,要跳的好看,就要勤于思考,勇于尝试,好的想法会利用规则优雅的处理事情。