drools规则引擎之约束
前面我们已经介绍了条件约束在Pattern中位置了,那么什么是条件约束呢?简单来说就是一个返回true或者false的表达式,比如下面的5小于6,就是一个约束条件。
Person( 5 < 6 ) |
从本质上来讲,它是JAVA表达式的一种增强版本(比如属性访问),同时它又有一些小的区别,比如equals方法和==的语言区别。下面我们就深入了解一下。
访问JavaBean中的属性
任何一个JavaBean中的属性都可以访问,不过对应的属性要提供getter方法或isProperty方法。比如:
Person( age == 50 ) // 与上面拥有同样的效果 Person( getAge() == 50 ) |
Drools使用java标准的类检查,因此遵循java标准即可。同时,嵌套属性也是支持的,比如:
Person( address.houseNumber == 50 ) // 与上面写法相同 Person( getAddress().getHouseNumber() == 50 ) |
在使用有状态session的情况下使用嵌套属性需要注意属性的值可能被其他地方修改。要么认为它们是不可变的,当任何一个父引用被插入到working memory中。或者,如果要修改嵌套属性值,则应将所有外部fact标记更新。在上面的例子中,当houseNumber属性值改变时,任何一个包含Address的Person需要被标记更新。
Java表达式
在pattern的约束条件中,可以任何返回结果为布尔类型的java表达式。当然,java表达式也可以和增强的表达式进行结合使用,比如属性访问。可以通过使用括号来更改计算优先级,如在任一逻辑或数学表达式中。
Person( age > 100 && ( age % 10 == 0 ) ) |
也可以直接使用java提供的工具方法来进行操作计算:
Person( Math.round( weight / ( height * height ) ) < 25.0 ) |
在使用的过程中需要注意,在LHS中执行的方法只能是只读的,不能在执行方法过程中改变FACT对象的值,否则会影响规则的正确执行。
Person( incrementAndGetAge() == 10 ) //不要像这样在比较的过程中更新Fact对象 |
另外,FACT对象的相关状态除了在working memory中可以进行更新操作,不应该在每次调用时使状态发生变化。
Person( System.currentTimeMillis() % 1000 == 0 ) // 不要这样实现 |
标准Java运算符优先级也适用于此处,详情参考下面的运算符优先级列表。所有的操作符都有标准的Java语义,除了==和!=。它们是null安全的,就相当于java中比较两个字符串时把常量字符串放前面调用equals方法的效果一样。
约束条件的比较过程中是会进行强制类型转换的,比如在数据计算中传入字符串“10”,则能成功转换成数字10进行计算。如果传入的值无法进行转换,比如传了“ten”,会抛出异常。
逗号分隔符
逗号可以对约束条件进行分组,它的作用相当于“AND”。
Person( ( age > 50, weight > 80 ) || height > 2 ) // 会编译错误
// 使用此种方法替代
Person( ( age > 50 && weight > 80 ) || height > 2 )
虽然“&&”和“,”拥有相同的功能,但是它们有不同的优先级。“&&”优先于“||”,“&&”和“||”又优先于“,”。建议优先使用“,”分隔符,因为它更利于阅读理解和引擎的操作优化。同时,逗号分隔符不能和其他操作符混合使用,比如:
// 2 person的age属性值相同
Person( $firstAge : age ) // 绑定
Person( age == $firstAge ) // 约束表达式
绑定变量
一个属性可以绑定到一个变量:
// 2 person的age属性值相同 Person( $firstAge : age ) // 绑定 Person( age == $firstAge ) // 约束表达式 |
前缀$只是个通用惯例,在复杂规则中可以通过它来区分变量和属性。为了向后兼容,允许(但不推荐)混合使用约束绑定和约束表达式。
// 不建议这样写 Person( $age : age * 2 < 100 ) // 推荐(分离绑定和约束表达式) Person( age * 2 < 100, $age : age ) |
使用操作符“==”来绑定变量,Drools会使用散列索引来提高执行性能。
内部类分组访问
通常情况,我们访问一个内部类的多个属性时会有如下的写法:
Person( name == “mark”, address.city == “london”, address.country == “uk” ) |
Drools提供的分组访问可以更加方便进行使用:
Person( name == “mark”, address.( city == “london”, country == “uk”) ) |
注意前缀’.’是用来区分嵌套对象约束和方法调用所必需的。
内部强制转换
在使用内部类的时候,往往需要将其转换为父类,在规则中可以通过“#”来进行强制转换:
Person( name == “mark”, address#LongAddress.country == “uk” ) |
上面的例子将Address强制转换为LongAddress.,使得getter方法变得可用。如果无法强制转换,表达式计算的结果为false。强制转换也支持全路径的写法:
Person( name == “mark”, address#org.domain.LongAddress.country == “uk” ) |
多次内部转换语法:
Person( name == “mark”, address#LongAddress.country#DetailedCountry.population > 10000000 ) |
也可以使用instanceof操作符进行判断,判断之后将进一步使用该属性进行比较。
Person( name == “mark”, address instanceof LongAddress, address.country == “uk” ) |
日期字符
规则语法中除了支持JAVA标准字符,同时也支持日期字符。Drools默认支持的日期格式为“dd-mmm-yyyy”,可以通过设置系统变量“drools.dateformat”的值来改变默认的日期格式。
Cheese( bestBefore < “27-Oct-2009” ) |
List和Map的访问
访问List:
// 和childList(0).getAge() == 18效果相同 Person( childList[0].age == 18 ) |
根据key访问map:
// 和credentialMap.get(“jsmith”).isValid()相同 Person( credentialMap[“jsmith”].valid ) |
&&和||
约束表达式可以通过&&和||来进行判断比较,用法与标准Java相似,当组合使用时,可通过括号来区分优先级。
Person( age > 30 && < 40 ) Person( age ( (> 30 && < 40) ||(> 20 && < 25) ) ) Person( age > 30 && < 40 || location == “london” ) |
相关技术视频
QQ技术交流2群:715840230
CSDN学院:《Drools7规则引擎进阶教程》
CSDN学院:《Drools7规则引擎入门教程》
CSDN学院:《Drools7系列优惠套餐》
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接