首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Drools 规约语言详解

2012-10-29 
Drools 规则语言详解1.概述:Drools 3采用了原生的规则语言,那是一种非XML文本格式。在符号方面,这种格式是

Drools 规则语言详解

1.概述:

Drools 3采用了原生的规则语言,那是一种非XML文本格式。在符号方面,这种格式是非常轻量的,并且通过“expanders”支持符合你问题域的Domain Specific Language(DSL)。这一章把焦点放在了Drools原生的规则格式。如果你想从技术上了解规则语言的机制,可以参考“drl.g”源文件,这是用Antlr3语法来描述规则语言。如果你使用Rule Workbench,内容助手将会为你完成大量的规则结构,例如输入“ru”,然后按ctrl+space,会为你建立规则结构。

1.1规则文件

一个规则文件通常是一个以.drl扩展名结尾的文件。在一个drl文件中,你可以有多条rules,functions等等。尽管如此,你也可以将你的规则分布在多个文件中,这有利于管理大量的规则。一个DRL文件是一个简单的文本文件。

1.2规则的结构

一个规则结构大致如下:

rule"name"
ATTRIBUTES
when
LHS
then
RHS
end

可以看到,这是非常简单的。通常的标点符号都是不需要的,甚至连“name”的双引号都是不需要的。ATTRIBUTES是简单的,也是可选的,来提示规则的行为方式。LHS是规则的条件部分,需要按照一定的语法来写。RHS基本上是一个允许执行Java语法的代码的块(以后将会支持groovy和C#)。任何在LHS中使用的变量都可以在RHS中使用。

注意:每行开始的空格是不重要的,除非在DSL(Domain Specific Language)语言中有特别的指明。

1.3Domain Specific Language

Domain Specific Language是对原生规则语言的加强。它们使用“expander”机制。Expander机制是一种可扩展的API。你可以使用.dsl文件,来提供从域或自然语言到规则语言和你的域对象的映射。你可以将.dsl文件看成是对你的域模型的映射。DSL提供了更高的规则可读性,你可以选择使用你自己创建的DSL,或者是原生的规则语言。

1.4保留字

在规则语言中存在一些保留字。你应该避免使用这些保留字,来命名规则文本中的域对象,属性,方法,功能。保留字如下:when,then,rule,end,contains,matches,and,or,modify,retract,assert,salience,function,query,exists,eval,agenda-group,no-loop,duration,->,not,auto-focus。

2.注释

2.1单行注释:

Drools 规约语言详解

2.2多行注释:

Drools 规约语言详解

3.Package

一个包是rule和其他相关结构,像import和global的集合。Package的成员之间通常都是相关联的。一个Package代表了一个命名空间(namespace),用来使给定的规则组之间保持唯一性。Package的名字本身就是命名空间,并且与文件或文件夹并无关联。

可以将来自不同规则源的规则装配在一起,前提是这些规则必须处在同一个命名空间中。尽管如此,一个通常的结构是将处于同一个命名空间中的所有规则都放在同一个相同的文件中。

下面的rail-road图显示了组成一个Package的所有组件。注意:一个package必须有一个命名空间,并且采用Java包名的约定。在一个规则文件中,各组件出现的位置是任意的,除了“package”和“expander”语句必须出现在任何一个规则之前,放在文件的顶部。在任何情况下,分号都是可选的。

Drools 规约语言详解

3.1 import

Drools 规约语言详解

Import语句的使用很像Java中的import语句。你需要为你要在规则中使用的对象,指定完整的路径和类名。Drools自动从相同命名的java包中引入所需的类。

3.2 expander

?

Drools 规约语言详解

expander语句是可选的,是用来指定Domain Specific Language的配置(通常是一个.dsl文件)。这使得解析器可以理解用你自己的DSL语言所写的规则。

3.3 global

Drools 规约语言详解

Global就是全局变量。如果多个package声明了具有相同标识符的global,那么它们必需是相同的类型,并且所有的引用都是相同的。它们通常用来返回数据,比如actions的日志,或者为rules提供所需的数据或服务。global并不是通过assert动作放入WorkingMemory的,所有当global发生改变时,引擎将不会知道。所以,global不能作为约束条件,除非它们的值是final的。将global错误的使用在约束条件中,会产生令人惊讶的错误结果。

注意:global只是从你的application中传入WorkingMemory的对象的命名实例。这意味着你可以传入任何你想要的对象。你可以传入一个service locator,或者是一个service本身。

下面的例子中,有一个EmailService的实例。在你调用规则引擎的代码中,你有一个EmailService对象,然后把它放入WorkingMemory。在DRL文件中,你声明了一个类型为EmailService的global,然后将它命名为“email”,像这样:global EmailService email;。然后在你的规则的RHS中,你可以使用它,像这样:email.sendSMS(number,message)等等。

4. Function

?

Drools 规约语言详解

Function是将代码放到你的规则源中的一种方法。它们只能做类似Helper类做的事(实际上编译器在背后帮你生成了Helper类)。在一个rule中使用function的主要优势是,你可以保持所有的逻辑都在一个地方,并且你可以根据需要来改变function(这可能是好事也可能是坏事)。Function最有用的就是在规则的RHS调用actions,特别是当那个action需要反复调用的时候。

一个典型的function声明如下:

functionStringcalcSomething(Stringarg){
return"hola!";
}

注意:“function”关键字的使用,它并不真正是Java的一部分。而function的参数就像是一个普通的method(如果不需要参数就不用写)。返回类型也跟普通的method一样。在一条规则(在它的RHS中,或可能是一个eval)中调用function,就像调用一个method一样,只需要function的名字,并传给它参数。

function的替代品,可以使用一个Helper类中的静态方法:Foo.doSomething(),或者以global的方式传入一个Helper类或服务的实例:foo.doSomething()(foo是一个命名的global变量)。


5. Rule

Drools 规约语言详解

Rule结构是最重要的结构。Rule使用了形如“IF”something“THEN”action(当然,我们的关键字是“when”和“then”)的形式。

一个规则在一个package中必须要有唯一的名字。如果一个名字中含有空格,那就需要将名字放在双引号中(最好总是使用双引号)。

Attribute是可选的(最好是每行只有一个Attribute)。

规则的LHS跟在“when”关键字的后面(最好是另起一行),同样RHS要跟在“then”关键字后面(最好也另起一行)。规则以关键字“end”结束。规则不能嵌套。

5.1 Left Hand Side

Left Hand Side其实就是规则的条件部分。LHS对应的rail-road图如下,我们在后面会做进一步解释:

Drools 规约语言详解

?

Drools 规约语言详解

5.2 Right Hand Side

Right Hand Side(RHS)就是规则的结果(consequence)或者动作(action)部分。RHS的目的是retract或add facts到WorkingMemory中,还有针对你的application的动作。实际上,RHS是当规则激发(fire)时执行的代码块。

在RHS中,你可以使用几个方便的method来改变WorkingMemory:

“modify(obj)”:告诉引擎一个对象已经发生变化,规则必须重新匹配(obj对象必须是出现在LHS中的对象);

“assert(new Something())”:将一个新的Something对象加入WorkingMemory;

“assertLogical(new Something())”:与assert方法类似。但是,当没有fact支持当前激发规则的真实性的时候,这个新对象会自动被retract,

“retract(obj)”:从WorkingMemory中移除一个对象。

这些方法都是宏指令,提供了到KnowledgeHelper实例的快捷方式(参考KnowledgeHelper接口)。KnowledgeHelper接口可以在RHS代码块中调用,通过变量“drools”。如果你在assert进引擎的JavaBean中加入“Property Change Listener”,在对象发生变化的时候,你就不用调用“modify”方法。

5.3 Rule Attributes

?

Drools 规约语言详解

5.3.1 no-loop

默认值:false

类型:boolean

当在rule的RHS中修改了一个fact,这可能引起这个rule再次被activate,引起递归。将no-loop设为true,就可以防止这个rule的Activation的再次被创建。

5.3.2 salience

默认值:0

类型:int

每个rule都可以设置一个salience整数值,默认为0,可以设为正整数或负整数。Salience是优先级的一种形式。当处于Activation队列中时,拥有高salience值的rule将具有更高的优先级。

5.3.3 agenda-group

默认值:MAIN

类型:String

Agenda group允许用户对Agenda进行分组,以提供更多的执行控制。只有具有焦点的组中的Activation才会被激发(fire)。

5.3.4 auto-focus

默认值:false

类型:boolean

当一个规则被activate(即Activation被创建了),如果这个rule的auto-focus值为true并且这个rule的agenda-group没有焦点,此时这个Activation会被给予焦点,允许这个Activation有fire的潜在可能。

5.3.5 activation-group

默认值:N/A

类型:String

当处于同一个activation-group中的第一个Activation fire后,这个activation-group中其余剩下的Activation都不会被fire。

5.3.6 duration

默认值:没有默认值

类型:long

5.4 Column

Drools 规约语言详解

Example5.1.Column

Cheese()
Cheese(type=="stilton",price<10)

?

?

一个Column由一个类的一个或多个域约束构成。第一个例子没有约束,它将匹配WorkingMemory中所有的Cheese实例。第二个例子对于一个Cheese对象有两个字面约束(Literal Constraints),它们被用“,”号隔开,意味着“and”。


Drools 规约语言详解

Example5.2.Bound Column

cheapStilton:Cheese(type=="stilton",price<10)

?

这个例子同前一个例子有点类似。但是在这个例子中,我们将一个变量绑定到匹配规则引擎的Cheese实例上。这意味着,你可以在另一个条件中使用cheapStilton,或者在rule的RHS中。

5.4.1 Field Constraints

Field Constraints使规则引擎可以从WorkingMemory中挑选出合适的Fact对象。一个Fact的“Field”必须符合JavaBean规范,提供了访问field的getter方法。你可以使用field的名字直接访问field,或者使用完整的方法名(省略括号)。

例如,以我们的Chess类为例,下面是等价的:Cheese(type = = …)和Cheese(getType = = …)。这意味着,你可以使用不太严格遵守JavaBean规范对象。尽管如此,你要保证accessor方法是不带参数的,以保证它不会改变对象的状态。

注意:如果一个field使用原始类型(primitive type),Drools将会把它们自动装箱成相应的对象(即使你使用java 1.4),但是在java 1.4下却不能自动拆箱。总的来说,尽量在rule所使用的类中,使用非原始类型的域。如果是使用java 5,就可以比较随意了,因为编译器会帮你执行自动装拆箱。

5.4.1.1 Operators

Drools 规约语言详解

有效的操作符是同域类型相关的。例如,对于日期域,“<”意味着“之前”。“matches”只适用于String域,“contains”和“excludes”只适用于Collection类型域。

5.4.1.2字面值约束(Literal Constraints)

最简单的域约束就是字面值约束,允许用户将一个field约束于一个已知值。

注意:你可以检查域是否为null,使用= =或!=操作符和字面值‘null’关键字。如,Cheese(type != null)。字面值约束,特别是“= =”操作符,提供了非常快的执行速度,因为可以使用散列法来提高性能。

?

Drools 规约语言详解

Numeric

所有标准的Java数字基本类型都可以用。

有效操作符:

·==

·!=

·>

·<

·>=

·<=

Example5.3 .Numeric Literal Constraint
Cheese(quantity==5)
Date

当前只对“dd-mm-yyyy”的日期格式提供默认支持。你可以通过指定drools.dateformat系统属性,来改变默认的日期格式。如果需要更多的控制,要用谓词约束(Predicate Constraint)。

有效操作符:

·==

·!=

·>

·<

·>=

·<=

Example5.4.Date Literal Constraint
Cheese(bestBefore<"27-Oct-2007")
String

可以使用任何有效的Java String。

有效操作符:

·==

·!=

Example5.5.String Literal Constraint
Cheese(type=="stilton")
Boolean

只能用“true”或“false”。0和1不能被识别,而且Cheese(smelly)也是不被允许的。

有效操作符:

·==

·!=

Example5.6Boolean Literal Constraint
Cheese(smelly==true)
Matches Operator

Matches操作符后面可以跟任何有效的Java正则表达式。

Example5.7.Regular Expression Constraint
Cheese(typematches"(Buffulo)?\\S*Mozerella")
Contains Operator and Excludes Operator

“contains”和“excludes”可以用来检查一个Collection域是否含有一个对象。

Example5.8.Literal Cosntraints with CollectionsCheeseCounter(cheesescontains"stilton")
CheeseCounter(cheesesexcludes"chedder")


5.4.1.3 Bound Variable Constraint

?

可以将Facts和它们的Fields附给一个Bound Variable,然后在后续的Field Constraints中使用这些Bound Variable。一个Bound Variable被称为声明(Declaration)。Declaration并不能和“matches”操作符合用,但却可以和“contains”操作符合用。

Example5.9.Bound Field using '==' operator

Person(likes:favouriteCheese)
Cheese(type==likes)

?

在上面的例子中,“likes”就是我们的Bound Variable,即Declaration。它被绑定到了任何正在匹配的Person实例的favouriteCheese域上,并且用来在下一个Column中约束Cheese的type域。可以使用所有有效的Java变量名,包括字符“$”。“$”经常可以帮助你区分Declaration和field。下面的例子将一个Declaration绑定到匹配的实例上,并且使用了“contains”操作符。注意:Declaratino的第一个字符用了“$”:

Example5.10Bound Fact using 'contains' operator

$stilton:Cheese(type=="stilton")
Cheesery(cheesescontains$stilton)


5.4.1.4 Predicate Constraints

Drools 规约语言详解

Predicate表达式可以使用任何有效的Java逻辑表达式。先前的Bound Declaration可以用在表达式中。

下面的例子将会找出所有男性比女性大2岁的pairs of male/femal people:

Example5.11. Predicate Constraints

Person(girlAge:age,sex=="F")
Person(boyAge:age->(girlAge.intValue()+2==boyAge.intValue()),sex=="M")


5.4.1.5 Return Value Constraints

一个Retrurn Value表达式可以使用任何有效的Java表达式,只要它返回一个对象,不能返回原始数据类型。如果返回值是原始数据类型,要先进行装箱。先前的Bound Declaration也可以使用在表达式中。

下面的例子跟上一节的例子一样,也将会找出所有男性比女性大2岁的pairs of male/femal people。注意:这里我们不用绑定boyAge,增加了可读性:

Example5.12.Return Value Constraints

Person(girlAge:age,sex=="F")
Person(age==(newInteger(girlAge.intValue()+2)),sex=="M")

?

5.5 Conditional Elements

Conditional Elements用来连接一个或多个Columns。

5.5.1“and”

Drools 规约语言详解

Example5.13. And

Cheese(cheeseType:type)&&Person(favouriteCheese==cheeseType)
Cheese(cheeseType:type)andPerson(favouriteCheese==cheeseType)


5.5.2“or”

?

Drools 规约语言详解

Example5.14.or

Person(sex=="f",age>60)||Person(sex=="m",age>65)
Person(sex=="f",age>60)orPerson(sex=="m",age>65)

?

?

Drools 规约语言详解

Example5.15.or with binding

pensioner:Person(sex=="f",age>60)||pensioner:Person(sex=="m",age>65)
pensioner:(Person(sex=="f",age>60)orPerson(sex=="m",age>65))

?

“or”Conditional Element的使用会导致多条rule的产生,称为sub rules。上面的例子将在内部产生两条规则。这两条规则会在WorkingMemory中各自独立的工作,也就是它们都能进行match,activate和fire。当对一个“or”Conditional Element使用变量绑定时,要特别小心,错误的使用将产生完全不可预期的结果。

可以将“OR”Conditional Element理解成产生两条规则的快捷方式。因此可以很容易理解,当“OR”Conditional Element两边都为真时,这样的一条规则将可能产生多个activation。

5.5.3“eval”

Drools 规约语言详解

Eval is essentially a catch all which allows any semantic code (that returns a primitive boolean) to be executed.在表达式中可以引用在LHS中出现的变量,和在rule package中的Functions。一个Eval应该是LHS中的最后一个Conditional Element。在一个rule中,你可以有多个eval。

Eval不能被索引,因此不能像Field Constraints那样被优化。尽管如此,当Functions的返回值一直在变化时,应该使用Eval,因为这在Field Constraints中时不允许的。如果规则中的其他条件都匹配,一个eval每次都要被检查。(现在还不理解到底eval要怎么用?)

Example5.16.eval

p1:Parameter()
p2:Parameter()
eval(p1.getList().containsKey(p2.getItem()))
eval(isValid(p1,p2))//thisishowyoucallafunctionintheLHS-afunctioncalled//"isValid"


5.5.4“not”

Drools 规约语言详解

not”是一阶逻辑的存在量词(first order logic’s Existential Quantifier),用来检查WorkingMemory中某对象的非存在性。现在,只有Columns可以放在not中,但是将来的版本会支持“and”和“or”。

Example5.17.No Buses

notBus()


Example5.18.No red Buses

notBus(color=="red")
not(Bus(color=="red",number==42))//bracketsareoptional


5.5.5“exists”

Drools 规约语言详解

“exists”是一阶逻辑的存在量词(first order logic’s Existential Quantifier),用来检查WorkingMemory中某对象的存在性。可以将“exists”理解为“至少有一个”(at least one…)。它的意义不同于只有Column本身,“Column”本身可以理解为“对于每一个…”(for each of …)。如果你对一个Column使用了“exists”,那么规则将只activate一次,而不管WorkingMeomry中有多少个数据匹配了那个条件。

现在,只有Columns可以放在“exists”中,但是将来的版本会支持“and”和“or”。

Example5.19.At least one Bus

existsBus()


Example5.20.At least one red Bus

existsBus(color=="red")


5.5.6“group”

Drools 规约语言详解

Group的作用相当于代数学中的“()”,显式的指明操作的顺序。

5.6再谈自动装箱和原始类型

Java 5支持在原始类型与其对应包装类之间的装拆箱。尽管如此,因为要让drools能够在J2SE 1.4下运行,我们不能依靠J2SE。因此,drools自己实现了自动装箱。被引用(即被Bound Variable绑定)的Field将被自动进行装箱(如果它们本身就是object,就不会有任何变化)。尽管如此,必须要注意的是,他们并不会被自动拆箱。

还有一点要注意的,就是对于ReturnValue Constraints,返回值的代码必须返回一个对象,而不能是一个原始类型。

6.Query

?

Drools 规约语言详解

一个query只包含了一个rule的LHS结构(你不用指定“when”或“then”)。这是查询WorkingMemory中匹配条件的Facts的简单的方法。

要得到结果,要使用WorkingMemory.getQueryResults(“name”)方法,其中“name”就是query的名字。Query的名字在RuleBase中是全局的,所以,do not add queries of the same name to different packages for the same RuleBase。

下面的例子创建了一个简单的query来查询所有年龄大于30的人:

Example6.1.Query People over the age of 30

query"peopleovertheageof30"
person:Person(age>30)
end

?

我们通过一个标准的循环来迭代一个返回的QueryResults对象。每一次的iterate将返回一个QueryResult对象。我们可以用QueryResult对象的get()方法来访问每个Column,通过传入Bound Declaration或index position。

Example6.2.Query People over the age of 30

QueryResultsresults=workingMemory.getQueryResults("peopleovertheageof30");
System.out.println("wehave"+results.size()+"peopleovertheageof30");

System.out.println("Thesepeopleareareover30:");

for(Iteratorit=results.iterator;it.hasNext();){
QueryResultresult=(QueryResult)it.next();
Personperson=(Person)result.get("person");
System.out.println(person.getName()+"\n");
}

?

原文地址:http://www.blogjava.net/guangnian0412/archive/2006/06/09/51756.html


热点排行