??xml version="1.0" encoding="utf-8" standalone="yes"?> ? Z规则的专家系l构成如?所C,推理引擎包括三部分:(x)模式匚w器(Pattern MatcherQ、议E(AgendaQ和执行引擎QExecution EngineQ。推理引擎通过军_哪些规则满事实或目标,q授予规则优先Q满事实或目标的规则被加入议程。模式匹配器军_选择执行哪个规则Q何时执行规则;议程理模式匚w器挑选出来的规则的执行次序;执行引擎负责执行规则和其他动作。和人类的思维相对应,推理引擎存在两者推理方式:(x)演绎法(F(tun)orward-ChainingQ和归纳法(Backward-ChainingQ。演l法从一个初始的事实出发Q不断地应用规则得出l论Q或执行指定的动作)(j)。而归Ux则是Ҏ(gu)假设Q不断地LW合假设的事实。Rete法是目前效率最高的一个Forward-Chaining推理法Q许多Java规则引擎都是ZRete法来进行推理计的。推理引擎的推理步骤如下Q?1)初始数据(factQ输入Working Memory?2)使用Pattern Matcher比较规则库(rule baseQ中的规则(ruleQ和数据QfactQ?3)如果执行规则存在冲突QconflictQ,卛_时激zM多个规则Q将冲突的规则放入冲H集合?4)解决冲突Q将Ȁzȝ规则按顺序放入Agenda?5)使用执行引擎执行Agenda中的规则。重复步??Q直到执行完毕所有Agenda中的规则。上q即是规则引擎的原始架构QJava规则引擎是从这一原始架构演变而来的?strong>2、规则引擎相x?/strong>规则引擎是一U根据规则中包含的指定过滤条Ӟ判断其能否匹配运行时ȝ实时条g来执行规则中所规定的动作的引擎。与规则引擎相关的有四个基本概念Qؓ(f)更好地理解规则引擎的工作原理Q下面将对这些概念进行逐一介绍?)信息元(Information UnitQ信息元是规则引擎的基本建筑块,它是一个包含了特定事g的所有信息的对象。这些信息包括:(x)消息、生事件的应用E序标识、事件生事件、信息元cd、相兌则集、通用Ҏ(gu)、通用属性以?qing)一些系l相关信息等{?2)信息服务QInformation ServicesQ信息服务生信息元对象。每个信息服务生它自己cd相对应的信息元对象。即特定信息服务Ҏ(gu)信息元所产生每个信息元对象有相同的格式,但可以有不同的属性和规则集。需要注意的是,在一台机器上可以q行许多不同的信息服务,q可以运行同一信息服务的不同实例。但无论如何Q每个信息服务只产生它自q型相对应的信息元?)规则集(Rule SetQ顾名思义Q规则集是许多规则的集合。每条规则包含一个条件过滤器和多个动作。一个条件过滤器可以包含多个qo(h)条g。条件过滤器是多个布?yu)(dng)表辑ּ的组合,其组合结果仍然是一个布?yu)(dng)类型的。在E序q行Ӟ动作会(x)在条件过滤器gؓ(f)真的情况下执行。除了一般的执行动作Q还有三cL较特别的动作Q它们分别是Q放弃动作(Discard ActionQ、包含动作(Include ActionQ和使信息元对象内容持久化的动作。前两种动作cd的区别将?.3规则引擎工作机制节介绍?)队列理器(Queue ManagerQ队列管理器用来理来自不同信息服务的信息元对象的队列。下面将研究规则引擎的这些相x件是如何协同工作的。如?所C,处理q程分ؓ(f)四个阶段q行Q信息服务接受事件ƈ其转化Z息元Q然后这些信息元被传l队列管理器Q最后规则引擎接收这些信息元q应用它们自w携带的规则加以执行Q直到队列管理器中不再有信息元? ? 处理q程协作?strong>3、规则引擎的工作机制
规则引擎的原?br />1、基于规则的专家pȝQRBESQ简?/strong>Java规则引擎h于基于规则的专家pȝQ而基于规则的专家pȝ又是专家pȝ的其中一个分支。专家系l属于h工智能的范畴Q它模仿人类的推理方式,使用试探性的Ҏ(gu)q行推理Qƈ使用人类能理解的术语解释和证明它的推理结论。ؓ(f)了更深入C解Java规则引擎Q下面简要地介绍Z规则的专家系l。RBES包括三部分:(x)Rule BaseQknowledge baseQ、Working MemoryQfact baseQ和Inference Engine。它们的l构如下pȝ所C:(x)
? 规则引擎工作机制Java规则引擎的工作机制与上述规则引擎机制十分cMQ只不过对上q概念进行了重新包装l合。Java规则引擎Ҏ(gu)交给引擎的Java数据对象q行(g)索,Ҏ(gu)q些对象的当前属性值和它们之间的关p,从加载到引擎的规则集中发现符合条件的规则Q创些规则的执行实例。这些实例将在引擎接到执行指令时、依照某U优先序依次执行。一般来ԌJava规则引擎内部׃面几个部分构成:(x)工作内存QWorking MemoryQ即工作区,用于存放被引擎引用的数据对象集合Q规则执行队列,用于存放被激zȝ规则执行实例;静态规则区Q用于存放所有被加蝲的业务规则,q些规则按照某U数据结构组l,当工作区中的数据发生改变后,引擎需要迅速根据工作区中的对象现状Q调整规则执行队列中的规则执行实例。Java规则引擎的结构示意图如图4所C?
? Java规则引擎工作机制当引擎执行时Q会(x)Ҏ(gu)规则执行队列中的优先序逐条执行规则执行实例Q由于规则的执行部分可能?x)改变工作区的数据对象,从而会(x)佉K列中的某些规则执行实例因为条件改变而失效,必须从队列中撤销Q也可能?x)激zd来不满条g的规则,生成新的规则执行实例q入队列。于是就产生了一U“动态”的规则执行链,形成规则的推理机制。这U规则的“链式”反应完全是由工作区中的数据驱动的?M一个规则引擎都需要很好地解决规则的推理机制和规则条g匚w的效率问题。规则条件匹配的效率军_了引擎的性能Q引擎需要迅速测试工作区中的数据对象Q从加蝲的规则集中发现符合条件的规则Q生成规则执行实例?982q美国卡耐基·梅隆大学的Charles L. Forgy发明了一U叫Rete法Q很好地解决了这斚w的问题。目前世界顶的商用业务规则引擎产品基本上都使用Rete法?/p>
<!--[if !supportLists]--> 1Q?span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"> <!--[endif]--> 概述Q?/span>
<!--[if !supportLists]--> 1.1 <!--[endif]--> 规则文g
一个规则文仉常是一个以
.drl
扩展名结文g。在一?/span>
drl
文g中,你可以有多条
rules
Q?/span>
functions
{等。尽如此,你也可以你的规则分布在多个文g中,q有利于理大量的规则。一?/span>
DRL
文g是一个简单的文本文g?/span>
1.2
规则的结?/span>
一个规则结构大致如下:(x)
可以看到Q这是非常简单的。通常的标点符号都是不需要的Q甚臌?/span> name ”的双引号都是不需要的?/span> ATTRIBUTES 是简单的Q也是可选的Q来提示规则的行为方式?/span> LHS 是规则的条g部分Q需要按照一定的语法来写?/span> RHS 基本上是一个允许执?/span> Java 语法的代码的块(以后会(x)支持 groovy ?/span> C Q)(j)。Q何在 LHS 中用的变量都可以在 RHS 中用?/span>
注意Q每行开始的I格是不重要的,除非?/span> DSL Q?/span> Domain Specific Language Q语a中有特别的指明?/span>
<!--[if !supportLists]--> 1.3 <!--[endif]--> Domain Specific Language
Domain Specific Language 是对原生规则语言的加强。它们用?/span> expander ”机制?/span> Expander 机制是一U可扩展?/span> API 。你可以使用 .dsl 文gQ来提供从域或自然语a到规则语a和你的域对象的映。你可以?/span> .dsl 文g看成是对你的域模型的映射?/span> DSL 提供了更高的规则可读性,你可以选择使用你自己创建的 DSL Q或者是原生的规则语a?/span>
1.4
保留?/span>
<!--[if !supportLists]--> 2. <!--[endif]--> 注释
Figure 2.1. Single line comment
<!--[if !supportLists]-->
3.
<!--[endif]--> Package
一个包?/span>
rule
和其他相关结构,?/span>
import
?/span>
global
的集合?/span>
Package
的成员之间通常都是相关联的。一?/span>
Package
代表了一个命名空_(d)
namespace
Q,用来使给定的规则l之间保持唯一性?/span>
Package
的名字本w就是命名空_(d)q且与文件或文g夹ƈ无关联?/span>
<!--[if !supportEmptyParas]-->
可以来自不同规则源的规则装配在一P前提是这些规则必d在同一个命名空间中。尽如此,一个通常的结构是处于同一个命名空间中的所有规则都攑֜同一个相同的文g中?/span>
下面?/span>
rail-road
图显CZl成一?/span>
Package
的所有组件。注意:(x)一?/span>
package
必须有一个命名空_(d)q且采用
Java
包名的约定。在一个规则文件中Q各lg出现的位|是L的,除了?/span>
package
”和?/span>
expander
”语句必d现在M一个规则之前,攑֜文g的顶部。在M情况下,分号都是可选的?/span>
Import 语句的用很?/span> Java 中的 import 语句。你需要ؓ(f)你要在规则中使用的对象,指定完整的\径和cd?/span> Drools 自动从相同命名的 java 包中引入所需的类?/span>
3.2 expander
Figure 3.3. expander
expander 语句是可选的Q是用来指定 Domain Specific Language 的配|(通常是一?/span> .dsl 文gQ。这使得解析器可以理解用你自q DSL 语言所写的规则?/span>
3.3 globalGlobal 是全局变量。如果多?/span> package 声明了具有相同标识符?/span> global Q那么它们必需是相同的cdQƈ且所有的引用都是相同的。它们通常用来q回数据Q比?/span> actions 的日志,或者ؓ(f) rules 提供所需的数据或服务?/span> global q不是通过 assert 动作攑օ WorkingMemory 的,所有当 global 发生改变Ӟ引擎不?x)知道。所以, global 不能作ؓ(f)U束条gQ除非它们的值是 final 的。将 global 错误的用在U束条g中,?x)生o(h)人惊讶的错误l果?/span>
<!--[if !supportEmptyParas]-->
注意Q?/span>
global
只是从你?/span>
application
中传?/span>
WorkingMemory
的对象的命名实例。这意味着你可以传入Q何你惌的对象。你可以传入一?/span>
service locator
Q或者是一?/span>
service
本n?/span>
<!--[if !supportEmptyParas]-->
下面的例子中Q有一?/span>
EmailService
的实例。在你调用规则引擎的代码中,你有一?/span>
EmailService
对象Q然后把它放?/span>
WorkingMemory
。在
DRL
文g中,你声明了一个类型ؓ(f)
EmailService
?/span>
global
Q然后将它命名ؓ(f)?/span>
email
”,像这P(x)
global EmailService email
Q。然后在你的规则?/span>
RHS
中,你可以用它Q像q样Q?/span>
email.sendSMS(number,message)
{等?/span>
4. Function
Function 是将代码攑ֈ你的规则源中的一U方法。它们只能做cM Helper cd的事Q实际上~译器在背后帮你生成?/span> Helper c)(j)。在一?/span> rule 中?/span> function 的主要优势是Q你可以保持所有的逻辑都在一个地方,q且你可以根据需要来改变 function Q这可能是好事也可能是坏事)(j)?/span> Function 最有用的就是在规则?/span> RHS 调用 actions Q特别是当那?/span> action 需要反复调用的时候?/span>
<!--[if !supportEmptyParas]-->
一个典型的 function 声明如下Q?/span>
<!--[if !supportEmptyParas]-->
注意Q?/span>
function
”关键字的用,它ƈ不真正是
Java
的一部分。?/span>
function
的参数就像是一个普通的
method
Q如果不需要参数就不用写)(j)。返回类型也跟普通的
method
一栗在一条规则(在它?/span>
RHS
中,或可能是一?/span>
eval
Q中调用
function
Q就像调用一?/span>
method
一P只需?/span>
function
的名字,q传l它参数?/span>
<!--[if !supportEmptyParas]-->
function
的替代品Q可以用一?/span>
Helper
cM的静态方法:(x)
Foo.doSomething()
Q或者以
global
的方式传入一?/span>
Helper
cL服务的实例:(x)
foo.doSomething()
Q?/span>
foo
是一个命名的
global
变量Q?/span>
<!--[if !supportEmptyParas]-->
http://labs.jboss.com/portal/index.html?ctrl:id=page.default.downloads&project=jbossrules
下蝲文g说明Q?/span>
JBoss Rules 3.0.1 Binaries (includes javadocs) Q?/span> 13MB Q?/span> 仅仅包含 JBoss Rules 的四个核心类库:(x)
<!--[if !supportLists]--> l <!--[endif]--> drools-core.jarQ?/span>核心引擎Q运行时lg。包含了RETE引擎?/span>LEAPS引擎Q?/span>
<!--[if !supportLists]--> l <!--[endif]--> drools-compiler.jarQ?/span>规则文g的编译组Ӟ构徏可执行的RuleBaseQ?/span>
<!--[if !supportLists]--> l <!--[endif]--> drools-jsr94.jarQ?/span>提供?/span>JSR-94的兼容实玎ͼ本质上是drools- compilerlg的包裹层。注意:(x)׃JSR94规约的限Ӟ不是所有的特点都可以通过此接口暴霌Ӏ?/span>
<!--[if !supportLists]--> l <!--[endif]--> drools-decisiontables.jarQ?/span>决策表的“编译”组Ӟ使用?/span>drools- compilerlgQ。支?/span>excel?/span>CSV输入格式?/span>
JBoss Rules 3.0.1 Binaries with dependencies (includes javadocs) Q?/span> 23 MB Q- 包含?/span> JBoss Rules 的核心类库和它们?/span> dependencies Q?/span>
<!--[if !supportLists]--> l <!--[endif]--> antlr-2.7.6.jar
<!--[if !supportLists]--> l <!--[endif]--> antlr-3.0ea8.jar
<!--[if !supportLists]--> l <!--[endif]--> colt-1.2.0.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-collections-3.1.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-io-1.1.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-jci-core-1.0-406301.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-jci-eclipse-3.2.0.666.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-jci-janino-2.4.3.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-lang-2.1.jar
<!--[if !supportLists]--> l <!--[endif]--> commons-logging-api-1.0.4.jar
<!--[if !supportLists]--> l <!--[endif]--> concurrent-1.3.4.jar
<!--[if !supportLists]--> l <!--[endif]--> core-3.2.0.666.jar
<!--[if !supportLists]--> l <!--[endif]--> janino-2.4.3.jar
<!--[if !supportLists]--> l <!--[endif]--> jsr94-1.1.jar
<!--[if !supportLists]--> l <!--[endif]--> jung-1.7.2.jar
<!--[if !supportLists]--> l <!--[endif]--> junit-3.8.1.jar
<!--[if !supportLists]--> l <!--[endif]--> poi-2.5.1-final-20040804.jar
<!--[if !supportLists]--> l <!--[endif]--> stringtemplate-2.3b6.jar
<!--[if !supportLists]--> l <!--[endif]--> xercesImpl-2.6.2.jar
<!--[if !supportLists]--> l <!--[endif]--> xml-apis-1.0.b2.jar
<!--[if !supportLists]--> l <!--[endif]--> xpp3-1.1.3.4.0.jar
<!--[if !supportLists]--> l <!--[endif]--> xstream-1.1.3.jar
如果你运行在 Java 1.5 环境下,有一些类库,例如 XML libraries Q可以不需要。需要注意的cd有:(x)
?/span>
JCI
”-q是
Apache Java Compiler Interface ,
提供了运行时~译能力。可以通过
PackageBuilderConfiguration
实例来设定采?/span>
eclipse
?/span>
janino
~译器,默认?/span>
eclipse
Q?/span>
?/span> POI ”-提供了解?/span> Excel 文g的能力;
?/span> antlr ”-提供了解析规则语a的能力?/span>
JBoss Rules IDE 3.0.1 Q?/span> 13 MB Q- q是 JBoss Rules ?/span> Eclipse 插gQ只支持 Eclipse 3.2 或以上版本。它提供了运?/span> JBoss Rules 的所?/span> dependencies 。你可以创徏一?/span> Rule Project Q它能够Z~写规则文g提供自动完成的功能,q且它ؓ(f)你提供了 Agenda view Q?/span> WorkingMemory view Q?/span> Global Data view Q你可以通过 eclipse 视图很清楚的看到 Agenda Q?/span> WorkingMemory ?/span> Global Data 的情c(din)?/span>
你还可以通过 update site 来自动安装这个插?/span> ,URL 是:(x)
http://anonsvn.labs.jboss.com/labs/jbossrules/updates/drools-ide-update/
<!--[if !supportEmptyParas]--> <!--[endif]-->
RuleBase 是一个运行时lgQ它包含了一个或多个 Package 对象。可以在M时刻一?/span> Package 对象加入或移?/span> RuleBase 对象。一?/span> RuleBase 对象可以在Q意时d例化一个或多个 WorkingMemory 对象Q在它的内部保持对这?/span> WorkingMemory 的弱引用?/span> WorkingMemory ׃pd子组件组成。当应用E序中的对象?/span> assert q?/span> WorkingMemory Q可能会(x)D一个或多个 Activation 的生,然后?/span> Agenda 负责安排q些 Activation 的执行。下图说明了上述q程Q?/span>
2Q构建(AuthoringQ:(x)
主要有三个类用来完成构徏q程Q?/span>DrlParser, XmlParser ?/span> PackageBuilder。两个解析器cM传入?/span>Reader实例产生descr AST模型?/span>PackageBuilder提供了简便的APIQ你可以忽略那两个cȝ存在。这两个单的Ҏ(gu)是:(x)?/span>addPackageFromDrl”和?/span>addPackageFromXml”,两个都只要传入一?/span>Reader实例作ؓ(f)参数。下面的例子说明了如何从classpath中的xml?/span>drl文g创徏一?/span>Package对象。注意:(x)所有传入同一?/span>PackageBuilder实例的规则源Q都必须是在相同?/span>package 命名I间Q?/span>namespaceQ中?/span>
PackageBuilder是可以配|的Q?/span>PackageBuilderConfiguration。通常Q你可以指定另一?/span>parent ClassLoader和用什么编译器Q?/span>compilerQ,默认?/span>Eclipse JDT。下面显CZ如何指定JANINO~译器:(x)
一?/span>RuleBase包含了多个将被用的规则包(packages of rulesQ。一?/span>RuleBase是可以序列化的,所以它可以被配|到JNDI或其他类似的服务。通常Q第一ơ用时Q一?/span>RuleBase被创建ƈ~存?/span>RuleBase?/span>RuleBaseFactory来实例化Q默认返回一?/span>ReteOO RuleBase。可以传入参数来指定采用ReteOO?/span>Leaps。然后,?/span>addPackageҎ(gu)加入Package实例。你可以加入有相同命名空_(d)namespaceQ的多个Package?/span>
一?/span> rulebase instance 是线E安全的Q所有你可以在你的应用中Q让一?/span> rulebase instance 在多个线E中׃n。对于一?/span> rulebase 的最通常的操作是产生一个新?/span> WorkingMemory ?/span>
q个 rulebase 保持着到它所产生?/span> WorkingMemoryd 的弱引用Q所以在长时间运行的 WorkingMemory 中,如果 rules 发生改变Q这?/span> WorkingMemory 可以即的根据最新的 rules q行更新Q而不必重?/span> WorkingMemory 。你也可以指?/span> RuleBase 不必保持一个弱引用Q但是你要保?/span> RuleBase 不用更新?/span>
M时候, Package 可以被加入或U除Q所有的改变都会(x)被反映到现存?/span> WorkingMemory 中。不要忘了调?/span> fireAllRules() ?/span> Activations Ȁ发?/span>
虽然有删除一个单独规则的Ҏ(gu)Q但是却没有加入一个单独规则的Ҏ(gu)Q要辑ֈq个目的只有加入一个只有一条规则的 package Q?/span>
<!--[if !supportEmptyParas]-->
RuleBaseConfigurator 可以指定 RuleBase 的附加行为。在加入 RuleBase 后, RuleBaseConfiguration 变成不可变对象?/span>
两个主要的属性是Q?/span> PROPERT_ASSERT_BEHAVIOR ?/span> PROPERTY_LOGICAL_OVERRIDE_BEHAVIOR Q在以后的部分中?x)解释?j)。所有的属性值都?/span> RuleBaseConfiguration cM的静态域帔R?/span>
WorkingMemory 是运行时规则引擎的主要类。它保持了所有被 asserted q?/span> WorkingMemory 的数据的引用Q直到取消( retracted Q?/span> WorkingMemory 是有状态对象。它们的生命周期可长可短。如果从一个短生命周期的角度来同一个引擎进行交互,意味着你可以?/span> RuleBase 对象来ؓ(f)每个 session 产生一个新?/span> WorkingMemory Q然后在l束 session ?/span> discard q个 WorkingMemory Q生一?/span> WorkingMemory 是一个廉L(fng)操作Q。另一UŞ式,是在一个相当长的时间中Q例如一?/span> conversation Q,保持一?/span> WorkingMemory Qƈ且对于新?/span> facts 保持持箋的更新。当你希?/span> dispose 一?/span> WorkingMemory 的时候,最好的实践是调用 dispose() Ҏ(gu)Q此?/span> RuleBase 中对它的引用会(x)被移除(管q是一个弱引用Q。不怎样最后它?yu)?x)被当成垃圾收集掉。术?/span> WorkingMemory Actions 代表了对 WorkingMemory ?/span> assertions Q?/span> retractions ?/span> modifications ?/span>
4.1 Facts
Facts 是从你的应用中,?/span> assert q?/span> WorkingMemory 中的对象Q?/span> beans Q?/span> Facts 是规则可以访问的L?/span> java 对象。规则引擎中?/span> facts q不是?/span> clone ?/span> facts Q它只是持有C的应用中数据的引用?/span> Facts 是你的应用数据?/span> String 和其他没?/span> getter ?/span> setter 的类不是有效?/span> Fact 。这L(fng)cM能用域U束Q?/span> Field Constraints Q,因ؓ(f)使用域约束要依靠 JavaBean 标准?/span> getter ?/span> setter 来同对象交互?/span>
4.2 Assertion
“Assertion?/span> 是将 facts 告诉 WorkingMemory 的动作,例如 WorkingMemory.assertObject (yourObject) 。当?/span> assert 一?/span> fact Q它?yu)被(g)查是否匹配规则。这意味着所有的匚w工作会(x)?/span> assert 的过E中完成。尽如此,当你完成 assert facts 之后Q你q要调用?/span> fireAllRules() ”方法来执行规则?/span>
当一个对象被 assert 后,?x)返回一?/span> FactHandle 。这?/span> FactHandle 是一个代表在 Working Memory 中你?/span> asserted Object 的o(h)牌( token Q。当你希?/span> retract 或?/span> modify 一个对象的时候,q个令牌让你用来?/span> WorkingMemory q行交互?/span>
WorkingMeomry 有两U?/span> assertion 模式Q?/span> Equality ?/span> Identity Q默认是 Identity Q?/span>
Identity 模式?/span> WorkingMemory 使用一?/span> IdentityHashMap 来存储所有的 asserted Objects 。这个模式下Q当 asserted ?/span> Object 是同一个实例时Q它q回同一?/span> FactHandle ?/span>
Equality 模式?/span> WorkingMemory 使用一?/span> HashMap 来存储所有的 asserted Objects 。这个模式下Q当 asserted ?/span> Object 相等Ӟ它返回同一?/span> FactHandle ?/span>
Q?/span> WorkingMemory.assertObject(yourObjcet) 只是q行 assertion 的一U?/span> regular Ҏ(gu)Q还存在有一U称?/span> logical assertion 的动作)(j)?/span>
4.3 Retraction
基本上就?/span> assert 的逆操作。当?/span> retract 一?/span> fact Q?/span> WorkingMemory 不再跟t那?/span> fact 。Q何被 activated q依赖那?/span> fact 的规则将被取消。注意:(x)完全有可能存在某条规则是依赖于一?/span> fact 的“不存在”( non existence Q。在q种情况下, retract 一?/span> fact 导致一条规则被ȀzR对一?/span> Fact q行 Retraction Q必ȝ assert 时返回的那个 FactHandle 做ؓ(f)参数?/span>
4.4 Modification
当一?/span> Fact 被修改了Q会(x)通知规则引擎q行重新处理。在规则引擎内部实际上是Ҏ(gu)?/span> Fact q行 retract Q然后对新的 Object 再进?/span> assert 。要使用 modifyObject() Ҏ(gu)来通知 Working Memory Q被改变?/span> Object q不?x)自己通知规则引擎。注意:(x) modifyObject() Ҏ(gu)L要把被修改的 Object 做ؓ(f)W二参数Q这允怽把一个不可变对象替换为另一个新对象?/span>
4.5 Globals
Global 是一个能够被传进 WorkingMemory 但不需?/span> assert 的命名对象。大多数q些对象被用来作为静态信息或服务。这些服务被用在一条规则的 RHS Q或者可能是从规则引擎返回对象的一U方法?/span>
setGlobal() Ҏ(gu)传进ȝ命名对象必须?/span> RuleBase 中所定义的具有相同的cdQ就是要同你的规则文件中?/span> Global 关键字所定义的类型相同)(j)Q否则会(x)抛出一?/span> RuntimeException 。如果一条规则在?/span> setGlobal 之前调用了定义的 Global Q会(x)抛出一?/span> NullPointerException ?/span>
4.6 Property Change Listener
如果你的 fact 对象?/span> JavaBean Q你可以为它们实C?/span> property change listener Q然后把它告诉规则引擎。这意味着Q当一?/span> fact 改变Ӟ规则引擎会(x)自动知道Qƈq行响应的动作(你不需要调?/span> modifyObject() Ҏ(gu)来通知 WorkingMemory Q?/span> Proxy libraries 会(x)帮助实现q一切。要?/span> Property Change Listener 生效Q还要将 fact 讄为动态( dynamic Q模式,通过?/span> true 做ؓ(f) assertObject() Ҏ(gu)的第二个参数来实玎ͼ(x)
然后要在 JavaBean 中加入一?/span> PropertyChangeSupport 实例Q和两个Ҏ(gu)Q?/span> addPropertyChangeListener() ?/span> removePropertyChangeListener() 。最后要?/span> JavaBean ?/span> setter Ҏ(gu)中通知 PropertyChangeSupport 所发生的变化。示例代码如下:(x)
Agenda ?/span> RETE 的一个特炏V在一?/span> WorkingMemory Action 发生Ӟ可能?x)有多条规则发生完全匚w。当一条规则完全匹配的时候,一?/span> Activation p创徏Q引用了q条规则和与其匹配的 facts Q,然后放进 Agenda 中?/span> Agenda 通过使用冲突解决{略Q?/span> Conflict Resolution Strategy Q来安排q些 Activations 的执行?/span>
引擎工作在一个?/span> 2 阶段”模式下Q?/span>
<!--[if !supportLists]--> 1Q?span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"> <!--[endif]--> WorkingMemory Actions Q?/span>assert新的factsQ修改存在的facts?/span>retract facts都是WorkingMemory Actions。通过在应用程序中调用fireAllRules()Ҏ(gu)Q会(x)使引擎{换到Agenda Evaluatioin阶段?/span>
<!--[if !supportLists]--> 2Q?span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"> <!--[endif]--> Agenda Evaluation Q尝试选择一条规则进行激发(fireQ。如果规则没有找到就退出,否则它就试Ȁ发这条规则,然后转换?/span>WorkingMemory Actions阶段Q直?/span>Agenda中ؓ(f)I?/span>
q个q程一直重复,直到 Agenda 是空的,此时控制权就回到应用E序中。。当 WorkingMemory Actions 发生Ӟ没有规则正在被激发?/span>
下图说明了这个@环的q程Q?/span>
Figure 5.2 . Two Phase Execution
5
Q?/span>
1 Conflict Resultion
当有多条 rules ?/span> agenda 中,需要解军_H。当Ȁ发一条规则时Q会(x)?/span> WorkingMemory 产生副作用。规则引擎需要知道规则要以什么顺序来Ȁ发(例如Q激?/span> rule A 可能?x)引?/span> rule B 被从 agenda 中移除。)(j)
Drools 采取的冲H解决策略有 2 U,按照优先U排列如下:(x) Salience Q?/span> LIFO Q后q先出)(j)。最易懂的策略是?/span> Salience ”,即优先Q?/span> user 可以为某?/span> rule 指定一个高一点的优先U(通过附给它一个比较大的数字)(j)。高 Salience ?/span> rule 会(x)被优先激发?/span>
5
Q?/span>
2 Agenda Groups
Agenda Groups 是划?/span> Agenda ?/span> rules Q其实是?/span> activations ”)(j)的一U方法。在L一个时刻,只有一?/span> group 拥有?/span> focus ”,q意味着只有在那?/span> group 中的 activations 才是有效的?/span>
Agenda Groups 是在 grouped rules 之间创徏一个“流”( flow Q的便的Ҏ(gu)。你可以在规则引擎中Q或是用 API 来切换具有焦点的l。如果你的规则有很明的多“阶D”( phases Q或多“序列”( sequences Q的处理Q可以考虑?/span> Agenda Groups 来达到这个目的?/span>
每次调用 setFocus() Ҏ(gu)的时候,那个 Agenda Group ׃(x)被压入一个堆栈,当这个有焦点的组为空Ӟ它就?x)被弹出Q然后下一个组׃(x)被执行。一?/span> Agenda Group 可以出现在堆栈的多个位置。默认的 Agenda Group 是?/span> MAIN ”,所有没有被指定 Agenda Group ?/span> Activations 都被攑ֈ那个l中Q这个组L被放在堆栈的W一个组Qƈ默认l予焦点?/span>
5
Q?/span>
3 Agenda Filters
Filter 必须实现 AgendaFilter 接口Q用来允许或止一?/span> activation 能够被激发?/span> Drools 提供了下面几U方便的默认实现Q?/span>
<!--[if !supportLists]--> · <!--[endif]--> RuleNameEndWithAgendaFilter
<!--[if !supportLists]--> · <!--[endif]--> RuleNameEqualsAgendaFilter
<!--[if !supportLists]--> · <!--[endif]--> RuleNameStartsWithAgendaFilter
要用一?/span> filter p在调?/span> fireAllRules() Ҏ(gu)的时候指定它。下面的例子对所有名字以?/span> Test ”结规则q行qo(h)Q?/span>
6Q事件模型( Event Model Q?/span>
Event 包里提供了规则引擎的事g机制Q包括规则激发,对象?/span> asserted {等。你可以使用事g机制来进?/span> AOP ~程?/span>
有两U类型的 Event Listener Q?/span> WorkingMemoryEventListener ?/span> AgendaEventListener ?/span>
对两?/span> EventListener 接口都提供了默认实现Q但在方法中q没有做M事。你可以l承q两个默认实现来完成你自q实现Q?/span> DefaultAgendaEventListener ?/span> DefaultWorkingMemoryEventListener 。下面代码说明了如何扩展一?/span> DefaultAgendaEventListner q把它加?/span> WorkingMemory 中,例子中只完成?/span> afterActivationFired() Ҏ(gu)Q?/span>
Drools 也提供了 DebugWorkingMemoryEventListener ?/span> DebugAgendaEventListener 两个实现c,在这两个cȝҎ(gu)中实C debug 信息的输出:(x)
Rete 在拉丁语中是 ”net?/span> Q有|络的意思?/span> RETE 法可以分ؓ(f)两部分:(x)规则~译Q?/span> rule compilation Q和q行时执行( runtime execution Q?/span>
~译法描述了规则如何在 Production Memory 中生一个有效的辨别|络。用一个非技术性的词来_(d)一个L别网l就是用来过滤数据。方法是通过数据在网l中的传播来qo(h)数据。在端节点会(x)有很多匹配的数据。当我们着|络向下赎ͼ匚w的数据将?x)越来越。在|络的最底部是终端节点( terminal nodes Q。在 Dr Forgy ?/span> 1982 q的论文中,他描qC 4 U基本节点:(x) root , 1-input, 2-input and terminal 。下图是 Drools 中的 RETE 节点cdQ?/span>
Figure 1. Rete Nodes
根节点( RootNode Q是所有的对象q入|络的入口。然后,从根节点立即q入?/span> ObjectTypeNode ?/span> ObjectTypeNode 的作用是使引擎只做它需要做的事情。例如,我们有两个对象集Q?/span> Account ?/span> Order 。如果规则引擎需要对每个对象都进行一个周期的评估Q那?x)浪费很多的旉。ؓ(f)了提高效率,引擎只让匹?/span> object type 的对象通过到达节点。通过q种Ҏ(gu)Q如果一个应?/span> assert 一个新?/span> account Q它不会(x)?/span> Order 对象传递到节点中。很多现?/span> RETE 实现都有专门?/span> ObjectTypeNode 。在一些情况下Q?/span> ObjectTypeNode 被用散列法进一步优化?/span>
Figure 2 . ObjectTypeNodes
ObjectTypeNode 能够传播?/span> AlphaNodes, LeftInputAdapterNodes ?/span> BetaNodes ?/span>
1-input 节点通常被称?/span> AlphaNode ?/span> AlphaNodes 被用来评估字面条Ӟ literal conditions Q。虽?dng)?/span> 1982 q的论文只提C相等条gQ指的字面上相等Q,很多 RETE 实现支持其他的操作。例如, Account.name = = “Mr Trout? 是一个字面条件。当一条规则对于一U?/span> object type 有多条的字面条gQ这些字面条件将被链接在一赗这是说Q如果一个应?/span> assert 一?/span> account 对象Q在它能到达下一?/span> AlphaNode 之前Q它必须先满第一个字面条件。在 Dr. Forgy 的论文中Q他?/span> IntraElement conditions 来表q。下面的图说明了 Cheese ?/span> AlphaNode l合Q?/span> name = = “cheddar?/span> Q?/span> strength = = “strong?/span> Q:(x)
Figure 3. AlphaNodes
Drools 通过散列法优化了?/span> ObjectTypeNode ?/span> AlphaNode 的传播。每ơ一?/span> AlphaNode 被加C?/span> ObjectTypeNode 的时候,׃字面| literal value Q作?/span> key Q以 AlphaNode 作ؓ(f) value 加入 HashMap 。当一个新的实例进?/span> ObjectTypeNode 的时候,不用传递到每一?/span> AlphaNode Q它可以直接?/span> HashMap 中获得正的 AlphaNode Q避免了不必要的字面(g)查?/span>
<!--[if !supportEmptyParas]-->
2-input 节点通常被称?/span> BetaNode ?/span> Drools 中有两种 BetaNode Q?/span> JoinNode ?/span> NotNode ?/span> BetaNodes 被用来对 2 个对象进行对比。这两个对象可以是同U类型,也可以是不同cd?/span>
我们U定 BetaNodes ?/span> 2 个输入称为左边( left Q和双Q?/span> right Q。一?/span> BetaNode 的左边输入通常?/span> a list of objects 。在 Drools 中,q是一个数l。右边输入是 a single object 。两?/span> NotNode 可以完成?/span> exists ’检查?/span> Drools 通过烦(ch)引应用在 BetaNodes 上扩展了 RETE 法。下囑ֱCZ一?/span> JoinNode 的用:(x)
Figure 4 . JoinNode
注意到图中的左边输入用到了一?/span> LeftInputAdapterNode Q这个节点的作用是将一?/span> single Object 转化Z个单对象数组Q?/span> single Object Tuple Q,传播?/span> JoinNode 节点。因为我们上面提到过左边输入通常?/span> a list of objects ?/span>
<!--[if !supportEmptyParas]-->
Terminal nodes 被用来表明一条规则已l匹配了它的所有条Ӟ conditions Q?/span> 在这点,我们说这条规则有了一个完全匹配( full match Q。在一些情况下Q一条带有“或”条件的规则可以有超q一个的 terminal node ?/span>
Drools 通过节点的共享来提高规则引擎的性能。因为很多的规则可能存在部分相同的模式,节点的共享允许我们对内存中的节点数量q行压羃Q以提供遍历节点的过E。下面的两个规则共享了部分节点Q?/span>
q里我们先不探讨q两?/span> rule 到的是什么意思,单从一个直观的感觉Q这两条 rule 在它们的 LHS 中基本都是一L(fng)Q只是最后的 favouriteCheese Q一条规则是{于 $cheddar Q而另一条规则是不等?/span> $cheddar 。下面是q两条规则的节点图:(x)
Figure 5 . Node Sharing
从图上可以看刎ͼ~译后的 RETE |络中, AlphaNode 是共享的Q?/span> BetaNode 不是׃n的。上面说的相{和不相{就体现?/span> BetaNode 的不同。然后这两条规则有各自的 Terminal Node ?/span>
<!--[if !supportEmptyParas]-->
RETE 法的第二个部分是运行时Q?/span> runtime Q。当一个应?/span> assert 一个对象,引擎数据传递到 root node 。从那里Q它q入 ObjectTypeNode q沿着|络向下传播。当数据匚w一个节点的条gQ节点就它记录到相应的内存中。这样做的原因有以下几点Q主要的原因是可以带来更快的性能。虽然记住完全或部分匚w的对象需要内存,它提供了速度和可伸羃性的特点。当一条规则的所有条仉满Q这是完全匚w。而只有部分条件满I是部分匚w。(我觉得引擎在每个节点都有其对应的内存来储存满节点条g的对象,q就造成了如果一个对象是完全匚wQ那q个对象׃(x)在每个节点的对应内存中都存有其映象。)(j)
2. Leaps 法Q?/span>
Production systems ?/span> Leaps 法使用了一U?/span> lazy ”方法来评估条gQ?/span> conditions Q。一U?/span> Leaps 法的修改版本的实现Q作?/span> Drools v3 的一部分Q尝试结?/span> Leaps ?/span> RETE Ҏ(gu)的最好的特点来处?/span> Working Memory 中的 facts ?/span>
古典?/span> Leaps Ҏ(gu)所有的 asserted ?/span> facts Q按照其?/span> asserted ?/span> Working Memory 中的序Q?/span> FIFO Q,攑֜d栈中。它一个个的检?/span> facts Q通过q代匚w data type ?/span> facts 集合来找出每一个相兌则的匚w。当一个匹配的数据被发现时Q系l记住此时的q代位置以备待会(x)的lP代,q且Ȁ发规则结果( consequence Q。当l果Q?/span> consequence Q执行完成以后,pȝ׃(x)l箋处理处于d栈顶部的 fact 。如此反复?/span>
Rules
一条规则是对商业知识的~码。一条规则有 attributes Q一?/span> Left Hand Side Q?/span> LHS Q和一?/span> Right Hand Side Q?/span> RHS Q?/span> Drools 允许下列几种 attributes Q?/span> salience Q?/span> agenda-group Q?/span> no-loop Q?/span> auto-focus Q?/span> duration Q?/font> activation-group ?/span>
规则可以通过 package 关键字同一个命名空_(d) namespace Q相兌Q其他的规则引擎可能U此则集Q?/span> Rule Set Q。一?/span> package 声明?/span> imports Q?/span> global 变量Q?/span> functions ?/span> rules ?/span>
Ҏ(gu)的数据和被修改的数据q行规则的匹配称为模式匹配( Pattern Matching Q。进行匹配的引擎UCؓ(f)推理机( Inference Engine Q。被讉K的规则称?/span> ProductionMemory Q被推理行匹配的数据UCؓ(f) WorkingMemory ?/span> Agenda 理被匹配规则的执行。推理机所采用的模式匹配算法有下列几种Q?/span> Linear Q?/span> RETE Q?/span> Treat Q?/span> Leaps ?/span>
Drools 采用?/span> RETE ?/span> Leaps 的实现?/span> Drools ?/span> RETE 实现被称?/span> ReteOO Q表C?/span> Drools ?/span> Rete 法q行了加强和优化的实现?/span>
一条规则的 LHS ?/span> Conditional Element 和域U束Q?/span> Field Constraints Q。下面的例子昄了对一?/span> Cheese Fact 使用了字面域U束Q?/span> Literal Field Constraint Q?/span>
上面的这个例子类gQ?/span>
规则引擎实现了数据同逻辑的完全解耦。规则ƈ不能被直接调用,因ؓ(f)它们不是Ҏ(gu)或函敎ͼ规则的激发是?/span> WorkingMemory 中数据变化的响应。结果( Consequence Q即 RHS Q作?/span> LHS events 完全匚w?/span> Listener ?/span>
?/span> rules 被加?/span> Productioin Memory 后, rules 被规则引擎用 RETE 法分解成一个图Q?/span>
?/span> Facts ?/span> assert q入 WorkingMemory 中后Q规则引擎找到匹配的 ObjectTypeNode Q然后将?/span> Fact 传播C一个节炏V?/span> ObjectTypeNode 拥有一块内存来保存所有匹配的 facts 。在我们的例子中Q下一个节Ҏ(gu)一个域U束Q?/span> Field Constraint Q, type = = “cheddar?/span> 。如果某?/span> Cheese 对象的类型不是?/span> cheddar ”,q个 fact 不?x)被传播到网l的下一个节炏V如果是?/span> cheddar ”类型,它将被记录到 AlphaNode 的内存中Qƈ传播到网l的下一个节炏V?/span> AlphaNode 是古?/span> RETE 术语Q它是一个单输入 / 单输出的节点。最后通过 AlphaNode ?/span> fact 被传播到 Terminal Node ?/span> Terminal Node 是最l节点,到此我们说这条规则被完全匚wQƈ准备Ȁ发?/span>
当一条规则被完全匚wQ它q没有立刻被Ȁ发(?/span> RETE 中是q样Q但?/span> Leaps 中它?x)立刻被Ȁ发)(j)。这条规则和与其匚w?/span> facts 激z被攑օ Agenda Q由 Agenda 来负责安排激?/span> Activations Q指的是 rule + the matched facts Q?/span>
下面的图很清楚的说明?/span> Drools 规则引擎的执行过E:(x)
数据?/span> assert q?/span> WorkingMemory 后,?/span> RuleBase 中的 rule q行匚wQ确切的说应该是 rule ?/span> LHS Q,如果匚w成功q条 rule q同和它匚w的数据(此时叫?/span> Activation Q一赯攑օ Agenda Q等?/span> Agenda 来负责安排激?/span> Activation Q其实就是执?/span> rule ?/span> RHS Q,上图中的菱Ş部分是?/span> Agenda 中来执行的, Agenda ׃(x)Ҏ(gu)冲突解决{略来安?/span> Activation 的执行顺序?/span>