我是Drools的新手,正在跟随这本书:掌握JBoss Drools 6
第2章展示了一个示例,我们创建了一个客户
和订单
,根据订单大小对客户进行分类,提供折扣并生成优惠券
。
该示例提到:
SILVER
客户HIGH_RANGE
商品SILVER
客户,因此创建了优惠券
SILVER
订单超过2件商品的客户提供10%的折扣以下是Drools规则:
add-discount. drl
rule "High Range order - 10% discount"
when
$o: Order($lines: orderLines, orderLines.size >= 2, $customer: customer, discount == null)
$c: Customer(category == Customer.Category.SILVER, this == $customer)
forall(
OrderLine(this memberOf $lines, $item: item)
Item(this == $item, category == Item.Category.HIGH_RANGE)
)
then
modify($o){
setDiscount(new Discount(10.0))
}
end
分类-客户-规则. dr l
rule "Classify Customer by order size"
when
$o: Order( orderLines.size >= 5, $customer: customer )
$c: Customer(this == $customer, category == Customer.Category.NA)
then
modify($c){
setCategory(Customer.Category.SILVER)
}
end
分类-项目-规则. dr l
rule "Classify Item - Low Range"
when
$i: Item(cost < 200, category == Category.NA)
then
$i.setCategory(Category.LOW_RANGE);
update($i);
end
rule "Classify Item - Mid Range"
when
$i: Item(cost > 200 && cost < 500, category == Category.NA)
then
$i.setCategory(Category.MID_RANGE);
update($i);
end
rule "Classify Item - High Range"
when
$i: Item(cost >= 500, category == Category.NA)
then
$i.setCategory(Category.HIGH_RANGE);
update($i);
end
优惠券创建
rule "Create Coupons for silver customer"
when
$o: Order($customer: customer)
$c: Customer(this == $customer, category == Customer.Category.SILVER)
then
Coupon x = new Coupon($c, $o, Coupon.CouponType.POINTS);
System.out.println(x);
insert(x);
end
执行的规则数量应该是8个。5个商品分类(5个规则)1个客户分类(1个规则)1个优惠添加(1个规则)1个优惠券创建(1个规则)
但是实际触发的规则数是9。这是我用来检查的测试。
@Test
void orderDiscountTest() {
KieSession kieSession = Util.getDefaultKieSession();
Order o = ModelFactory.getOrderWithFiveHighRangeItems();
kieSession.insert(o.getCustomer());
kieSession.insert(o.getOrderLines().get(0));
kieSession.insert(o.getOrderLines().get(1));
kieSession.insert(o.getOrderLines().get(2));
kieSession.insert(o.getOrderLines().get(3));
kieSession.insert(o.getOrderLines().get(4));
kieSession.insert(o.getOrderLines().get(0).getItem());
kieSession.insert(o.getOrderLines().get(1).getItem());
kieSession.insert(o.getOrderLines().get(2).getItem());
kieSession.insert(o.getOrderLines().get(3).getItem());
kieSession.insert(o.getOrderLines().get(4).getItem());
kieSession.insert(o);
int fired = kieSession.fireAllRules();
assertEquals(8, fired);
assertEquals(Customer.Category.SILVER, o.getCustomer().getCategory());
assertNotNull(o.getDiscount());
assertEquals(10.0, o.getDiscount().getPercentage());
assertEquals(Item.Category.HIGH_RANGE, o.getOrderLines().get(0).getItem().getCategory());
assertEquals(Item.Category.HIGH_RANGE, o.getOrderLines().get(1).getItem().getCategory());
assertEquals(Item.Category.HIGH_RANGE, o.getOrderLines().get(2).getItem().getCategory());
assertEquals(Item.Category.HIGH_RANGE, o.getOrderLines().get(3).getItem().getCategory());
assertEquals(Item.Category.HIGH_RANGE, o.getOrderLines().get(4).getItem().getCategory());
Collection<Coupon> coupons = Util.getFactsFromSession(kieSession, Coupon.class);
coupons.forEach(x -> System.out.println(x));
assertEquals(1, coupons.size());
}
优惠券创建规则触发了两次,不明白为什么,我这样查看了触发规则后创建的优惠券数量:
Collection<Coupon> coupons = Util.getFactsFromSession(kieSession, Coupon.class);
coupons.forEach(x -> System.out.println(x));
并且我在优惠券创建规则中也有打印语句。
这就是我得到的:
12:54:47.607 [main] DEBUG org.drools.core.impl.KnowledgeBaseImpl - Starting Engine in PHREAK mode
12:54:47.730 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is nw FIRING_ALL_RULES
12:54:47.732 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.795 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
Coupon created com.example.droolstut.model.Coupon@b14cb989
12:54:47.797 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.797 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.799 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
Coupon created com.example.droolstut.model.Coupon@424f842a
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is nw HALTING
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is nw INACTIVE
Coupon found: com.example.droolstut.model.Coupon@424f842a
Coupon found: com.example.droolstut.model.Coupon@424f842a
所以,看起来规则执行了两次,但执行后只找到了第二张优惠券。
但是,当我在调试模式下运行测试时,优惠券规则只执行一次,并且触发的规则总数也是8个。
我错过什么了吗?我很感激任何帮助。
编辑1:
我在添加not(Coupon(…))
条件后打印了订单和客户。项目对象看起来不同。
Order(orderId=1, date=Thu Jan 01 05:30:00 IST 1970, customer=Customer(customerId=1, age=40, name=John Doe, email=johndoe@example.com, category=SILVER), orderLines=[OrderLine(item=com.example.droolstut.model.Item@38c70399, quantity=1), OrderLine(item=com.example.droolstut.model.Item@5245e3d4, quantity=1), OrderLine(item=com.example.droolstut.model.Item@6bc4c40f, quantity=1), OrderLine(item=com.example.droolstut.model.Item@8543a44a, quantity=1), OrderLine(item=com.example.droolstut.model.Item@9ec28485, quantity=1)], state=PENDING, discount=null) Customer(customerId=1, age=40, name=John Doe, email=johndoe@example.com, category=SILVER)
com.example.droolstut.model.Coupon@2753dad2
12:16:02.399 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:16:02.400 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:16:02.402 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:16:02.408 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
Order(orderId=1, date=Thu Jan 01 05:30:00 IST 1970, customer=Customer(customerId=1, age=40, name=John Doe, email=johndoe@example.com, category=SILVER), orderLines=[OrderLine(item=com.example.droolstut.model.Item@5dc27ea9, quantity=1), OrderLine(item=com.example.droolstut.model.Item@77415ee4, quantity=1), OrderLine(item=com.example.droolstut.model.Item@90c03f1f, quantity=1), OrderLine(item=com.example.droolstut.model.Item@aa3f1f5a, quantity=1), OrderLine(item=com.example.droolstut.model.Item@c3bdff95, quantity=1)], state=PENDING, discount=Discount(percentage=10.0)) Customer(customerId=1, age=40, name=John Doe, email=johndoe@example.com, category=SILVER)
com.example.droolstut.model.Coupon@10e5ecb2
编辑2:
我正在为getter/setter/Equals使用lombok
@Override
public int hashCode() {
int hash = 3;
hash = 17 * hash + Objects.hashCode(this.orderId);
hash = 17 * hash + Objects.hashCode(this.date);
hash = 17 * hash + Objects.hashCode(this.customer);
hash = 17 * hash + Objects.hashCode(this.orderLines);
hash = 17 * hash + Objects.hashCode(this.state);
hash = 17 * hash + Objects.hashCode(this.discount);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Order other = (Order) obj;
if (!Objects.equals(this.orderId, other.orderId)) {
return false;
}
if (!Objects.equals(this.date, other.date)) {
return false;
}
if (!Objects.equals(this.customer, other.customer)) {
return false;
}
if (!Objects.equals(this.orderLines, other.orderLines)) {
return false;
}
if (this.state != other.state) {
return false;
}
if (!Objects.equals(this.discount, other.discount)) {
return false;
}
return true;
}
当一本关于Drools的书出版时,它已经过时了。Drools有很棒的留档,你应该把它作为你的主要来源,而不是一本关于Drools旧版本的旧书。(Drools的当前版本是Drools 8。你的书是为Drools 6写的。)
我建议人们不要使用书籍的另一个原因是因为它们往往很糟糕。这个也不例外
你触发比你想象的更多规则的原因是,“高范围订单-10%折扣”强制重新评估工作记忆,而“为白银客户创建优惠券”没有任何东西可以阻止它多次触发。
“High Range…”调用“修改”,它会重新触发所有带有修改项目的规则。由于“创建优惠券…”仍然有效,它将触发两次。(它会触发一次,然后一旦“High Range…”强制重新评估工作内存,它将触发第二次。)您还会看到工作内存中的优惠券数量是您预期的两倍——将有2张优惠券而不是1张,因为您两次触发优惠券规则。
为了防止这种情况发生,您需要使“创建优惠券…”不能有效触发两次。(也就是说,一旦触发,就不能再次触发。)您可以这样做,例如,不允许两张相同的优惠券。或者您可以只允许一张优惠券。取决于您的用例。
一种方法可能是:
rule "Create Coupons for silver customer"
when
$o: Order($customer: customer)
$c: Customer(category == Customer.Category.SILVER)
// don't fire if this coupon already exists; I guessed on the variable names
not( Coupon( customer == $c, order == $o, type == Coupon.CouponType.POINTS) )
then
Coupon x = new Coupon($c, $o, Coupon.CouponType.POINTS);
insert(x);
end
在现实世界中,你希望你的规则保持简单。不要把整个厨房水槽粘在一个KieBase中。单一责任原则是这里的关键。你的“物品分类”规则应该与你的客户分类规则分开,应该与你的折扣规则分开。
调用update
也是让自己陷入无限循环并大大增加内存/cpu开销的好方法。在我的上一家公司,它不仅被认为是代码异味和不良做法,而且实际上被彻底禁止(我们在代码审查中检查过它。)