在上一节中,我们讲了最一个最简单的例子,单个对象的实例。其实这个对象,在Drools中有个专业名词叫:Fact
。 Fact
对象的方式来实现规则引擎与业务数据的交互,对于Fact对象就是普通的具有若干个属性及其对应的getter与setter方法的JavaBean对象,所以注意啦,它不是我们业务Domain对象,他应该具备规则所涉及的一个或者多个Domain对象的多个属性。
另外在drools中KieSession分为2种:
- stateless 无状态Session
- stateful 有状态的Session,长期存活,允许重复使用。
从官方的说法来看,2者除了在处理大数据量时有一定区别外,平时几无区别,statelessSession也是基于statefulSession的,那么有状态的Session有以下几个常用的用途:
- Monitoring Stock market monitoring and analysis for semi-automatic buying.
- Diagnostics Fault finding, medical diagnostics
- Logistics Parcel tracking and delivery provisioning
- Compliance Validation of legality for market trades.
这一节我们就通过monitoring user case(监视器用法)讲讲多个对象的之间的相互协作(关联)。我们以房屋发生火灾报警时,蓬头自动出水灭火的场景来模拟监视器用法。
###1.定义Fact
package me.drools.demo.monitoring;
public class Room {
private String name
// getter and setter methods here
}
public class Sprinkler {
private Room room;
private boolean on;
// getter and setter methods here
}
public class Fire {
private Room room;
// getter and setter methods here
}
public class Alarm {
}
###2.定义规则 定义monitoring.drl
package me.drools.monitoring
import me.drools.demo.monitoring.*
dialect "mvel"
rule "When there is a fire turn on the sprinkler"
when
Fire($room:room)
$sprinkler : Sprinkler(room == $room,on == false)
then
modify($sprinkler){setOn(true)}
System.out.println("Turn on the sprinkler for room " + $room.getName());
end
rule "When the fire is gone turn off the sprinkler"
when
$room : Room( )
$sprinkler : Sprinkler( room == $room, on == true )
not Fire( room == $room )
then
modify( $sprinkler ) { setOn( false ) };
System.out.println( "Turn off the sprinkler for room " + $room.getName() );
end
rule "Raise the alarm when we have one or more fires"
when
exists Fire()
then
insert( new Alarm() );
System.out.println( "Raise the alarm" );
end
rule "Cancel the alarm when all the fires have gone"
when
not Fire()
$alarm : Alarm()
then
delete( $alarm );
System.out.println( "Cancel the alarm" );
end
rule "Status output when things are ok"
when
not Alarm()
not Sprinkler( on == true )
then
System.out.println( "Everything is ok" );
end
这一部分其实没有啥好讲的,跟Java语法都是非常的接近,注意下几个关键字的用法:modify
,not
,exists
,delete
,insert
其实modify
语句也可以直接写成java方法调用,比如 $sprinkler.setOn(true);
###3.Junit测试类
public class FireTest {
@Test
public void testFire(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
KieSession ksession = kContainer.newKieSession();
String[] names = new String[]{"kitchen", "bedroom", "office", "livingroom"};
Map<String,Room> name2room = new HashMap<String,Room>();
for( String name: names ){
Room room = new Room( name );
name2room.put( name, room );
ksession.insert( room );
Sprinkler sprinkler = new Sprinkler( room );
ksession.insert( sprinkler );
}
ksession.fireAllRules();
Fire kitchenFire = new Fire( name2room.get( "kitchen" ) );
Fire officeFire = new Fire( name2room.get( "office" ) );
FactHandle kitchenFireHandle = ksession.insert( kitchenFire );
FactHandle officeFireHandle = ksession.insert( officeFire );
ksession.fireAllRules();
ksession.delete( kitchenFireHandle );
ksession.delete( officeFireHandle );
ksession.fireAllRules();
}
}
通过运行Junit测试,对照drl文件,观察打印语句,包括规则执行的次数,感受上面提到的几个关键字的用法,以及如何通过这些关键字在rule之间相互流转。
###4.Cross Products
其实就是笛卡尔乘积,主要涉及到我们在when
语句块里面条件表达式的编写,假设我们现在增加这样一个规则:
rule "Show Sprinklers"
when
$room : Room()
$sprinkler : Sprinkler()
then
System.out.println( "room:" + $room.getName() +
" sprinkler:" + $sprinkler.getRoom().getName() );
end
则会打印:
room:office sprinkler:office
room:office sprinkler:kitchen
room:office sprinkler:livingroom
room:office sprinkler:bedroom
room:kitchen sprinkler:office
room:kitchen sprinkler:kitchen
room:kitchen sprinkler:livingroom
room:kitchen sprinkler:bedroom
room:livingroom sprinkler:office
room:livingroom sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:livingroom sprinkler:bedroom
room:bedroom sprinkler:office
room:bedroom sprinkler:kitchen
room:bedroom sprinkler:livingroom
room:bedroom sprinkler:bedroom
那么上面这个场景在学习SQL的时候是不是也见过?就等同于select * from Room, Sprinkler
那么我们现在把上面的规则改一下:
rule "Show Sprinklers"
when
$room : Room()
$sprinkler : Sprinkler( room == $room )
then
System.out.println( "room:" + $room.getName() +
" sprinkler:" + $sprinkler.getRoom().getName() );
end
现在打印信息如下:
room:office sprinkler:office
room:kitchen sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:bedroom sprinkler:bedroom
等同于SQL:select * from Room, Sprinkler where Room == Sprinkler.room.
有了这个对照,其实对于编写一般的Rule相信已经没有问题了。