这个是DRools4.0的官方word文档参考哦。很详细全面的。 ````````````` 第2章. 规则引擎 2.1. 什么是规则引擎? 2.1.1. 背景介绍 A.I.(Artificial Intelligence)是一个关注于“使计算机像人类一样思考“的广泛的研究领域,包括Neural Networks(神经网络), Genetic Algorithms(遗传算法), Decision Trees(决策树), Frame Systems(框架系统) and Expert Systems(专家系统)。Knowledge representation(知识呈现)是A.I.的一部分,关注于如何呈现和操纵知识。专家系统使用知识表示把知识编码简化成一个可用于推理的知识库──比如,我们可以用知识库处理数据以推出结论。专家系统又叫基于知识的系统、基于知识的专家系统,并被认为是A.I.的一个应用。开发一个专家库系统的过程被称为知识工程。EMYCIN是最早的专家系统Shell(外壳)之一,它从医学诊断专家系统MYCIN发展而来。早期的专家系统有自己的logic hard coded "shells"(逻辑硬件编码外壳),把逻辑与系统相分离,为用户输入提供一个简单的使用环境。Drools是一个使用基于规则的方法实现的专家系统的规则引擎,更准确的说属于产生式规则系统。 术语“产生式规则”从形式语法中产生,形式语法——使用一种抽象结构来准确描述形式语言 (wikipedia)。 The term "Production Rule" originates from formal grammar - where it is described as "an abstract structure that describes a formal language precisely, i.e., a set of rules that mathematically delineates a (usually infinite) set of finite-length strings over a (usually finite) alphabet" (wikipedia). 业务规则管理系统在普通的规则引擎基础上通过提供—集中的业务用户,规则建立系统,管理,发布,协作,分析和终端用户工具等来达到更高的附加价值;使得企业能够以更顺利的方式引入规则引擎 规则引擎这个术语是非常不明确的,因为任何以任意形式使用能够应用于数据生成结果的规则的系统都可以称为规则引擎。包括像表单验证和动态表达式引擎这样的简单系统都可以称之为规则引擎。作者Malcolm Chisholm 的著作《How to Build a Business Rules Engine (2004)》就例证了这种不明确性。该书实际是讲述如何为管理校验规则而建立和维护一个数据库计划。该书中接着展示了如何根据这些校验规则产生VB代码来校验输入—在某些情况下这是很有用的,作者十分惊讶于这种不明确性,在没有觉察到各种规则引擎间细微不同之处时,他希望能发现其中的奥秘以帮助增强Drools引擎。JBoss jBPM(业务流程管理工具)在它的判断节点上使用表达式和代理引用来控制工作流中的事务。通过在每个节点上求值以决定分支的流向—这同样是一个规则引擎。产生式规则系统既是一种规则引擎,又是一个专家系统,而之前提到的校验器和表达式求值规则引擎不是专家系统 产生式规则系统完全关注于精确表达propositional(命题)和first order logic(一阶逻辑)的知识表示,不存在含糊不清的定义。产生式规则系统的核心是一个能够处理大量规则和事实的推理引擎。推理引擎将事实、数据与产生式规则(也可以叫做产生式,或干脆叫规则)进行匹配,以推出结论。产生式规则是一个用一阶逻辑进行知识呈现的二元结构。 when <conditions> then <actions> 将新的或已存在的事实与产生式规则进行匹配的过程被称为模式匹配,这个过程由推理机完成。推理机使用的用于模式匹配的算法有很多,包括: • Linear 线性的 • Rete 网状 • Treat • Leaps 叶状 Drool实现了Rete和Leaps算法;Leaps是试验性质的,因为它是个十分新的算法。Drools中的Rete算法被称为ReteOO,表示Drools为面向对象系统(Object Oriented systems)增强并优化了Rete算法。其它基于Rete算法的引擎也有他们对Rete算法进行独有增强后的市场术语,比如RetePlus,ReteIII。要知道象ReteIII这样的名字纯粹是市场性的,不像原始版本的Rete算法,它们没有公布实现细节,这一点很重要;因此,问类似于“Drools是用ReteIII实现的吗?”的问题是没有任何意义的。最寻常的对Rete算法的增强在下面这篇文章中有论述"Production Matching for Large Learning Systems (Rete/UL)" (1995) by Robert B. Doorenbos。 规则保存在Production Memory(规则库)中,推理机要匹配的facts(事实)保存在Working Memory(工作内存)中。事实被插入到工作内存中后,可能被修改或删除。一个有大量规则和事实的系统可能会很多规则被满足,这些规则被称为具有冲突性。Agenda通过(冲突决策策略)管理这些冲突规则的执行顺序。 Figure 2.1. 基础Rete网络 产生式规则系统的推理机是有状态的并且能够维持其中状态值的准确性,称为Truth Maintence(真值维护)。规则引擎中的动作仅当其依赖的约束为真值情况下执行,如果约束不再有效,则推论动作不会执行。"Honest Politician"(Drools提供的一个示例)就是这种真值维护的示范,在示例中规定,只要在存在诚实的政治家时,一个政体才有希望。 when an honest Politician exists then logically assert Hope when Hope exists then print "Hurrah!!! Democracy Lives" when Hope does not exist then print "Democracy is Doomed" 产生式规则系统有两种执行方法──正向推理和逆向推理,两种方法都使用的系统称为混合型产生式规则系统。理解这两种操作方法是理解产生式规则系统之所以不同和怎样从中选择最合适的系统的关键。正向推理是数据驱动的,facts事实被传递到工作空间中,在那里有一个或多个规则与这些事实匹配,并由Agenda安排执行—我们从一个事实开始,传递事实,最后得到一个结论。Drools是基于正向推理的规则引擎。 Figure 2.2. 正向推理 逆向推理是由目标驱动的,这意味着我们从一个引擎需要满足的结论开始进行推理。在这个结论不能满足时,将搜索一些能够满足的结论来推理,称为子目标,这些子目标将帮助完成当前目标的某些未知部分——引擎持续这个过程,直到最初的结论被证明或没有可证明的子目标。Prolog是逆向驱动型的引擎。Drools将在下一个主要版本中加入对逆向推理的支持。 Figure 2.3. 逆向推理 2.2. 为何使用规则引擎? 人们常常会问到: 1. 何时应当使用规则引擎? 2. 规则引擎与"if...then" 这样的硬编码比起来有什么优点? 3. 为何应当使用规则引擎取代脚本框架? 我们尝试通过以下说明来解答这些问题 2.2.1. 规则引擎的优点 • 声明式编程 规则引擎允许你描述做什么而不是如何去做。 这里的主要优点是使用规则更加容易对复杂的问题进行表述,并得到验证。 (规则比编码更容易阅读). 规则系统能够解决非常非常困难的问题,并提供了方案怎样达到和在解决问题的方向上所作的每一个决定的原因(这对于类似神经网络这样的AI系统来说不容易达到) • 逻辑与数据分离 数据保存在系统对象中,逻辑保存在规则中。这根本性的打破了面向对象系统中将数据和逻辑耦合起来的局面,这点是有利的也是不利的,在于你的观察角度。这样做的结果是,将来逻辑发生改变时更容易被维护,因为逻辑保存在规则中。这点在逻辑是跨领域或多领域中使用时尤其有用。通过将逻辑集中在一个或数个清晰的规则文件中,取代了之前分散在代码中的局面。 • 速度及可测量性 Rete算法、Leaps算法,以及由此衍生出来的Drools的Rete、Leaps算法,提供了对系统数据对象非常有效率的匹配。这些都是高效率尤其当你的数据是不完全的改变(规则引擎能够记得之前的匹配)。这些算法经过了大量实际考验的证明。 • 知识集中化 通过使用规则,将建立一个可执行的规则库。这意味着规则库代表着现实中的业务策略的唯一对应,理想情况下可读性高的规则还可以被当作文档使用。 • 工具集成 例如Eclipse(将来可能在基于Web的界面上)这样的工具为规则的修改与管理、即时获得反馈、内容验证与修补提供了办法。审查与调试工具同样也可用了。 • 解释机制 通过将规则引擎的决断与决断的原因一起记录下来,规则系统提供了很好的“解释机制”。 • 易懂的规则 通过建立对象模型以及DSL(域定义语言),你可以用接近自然语言的方式来编写规则。这让非技术人员与领域专家可以用他们自己的逻辑来理解规则(因为程序的迷宫已经被隐藏起来了) 。 2.2.2. 何时应当使用规则引擎? 对这个问题最简短的回答就是“当没有令人满意的传统的程序设计方法能够解决这个问题时”。下面上对所谓没有传统解决方法的一个描述: • 对于传统代码来说,问题需要的精确度太高。 这种问题可能并不复杂,但是你找不到一种稳定的方法去建立它。 • 问题超越了任何有明显运算法则的方案。 它是一个难以解决的复杂问题,没有明显的传统解决方案或者问题没有一个准确的定论。 • 业务逻辑经常发生改变 逻辑本身是简单的(但不是指过于简单),但是规则经常发生变化。在许多软件组织中正式版本的间隔是较长并且较少的,规则可以在适当的安全前提下帮助提供一定的敏捷性。 • 领域专家(或者业务分析师)是非技术人员 领域专家通常对业务规则和流程具有很好的认知。他们通常是不了解软件技术的人员,但是具有很好的逻辑性。规则能够让他们用自己的术语来描述业务逻辑。当然他们仍然需要严密的思考和良好的逻辑思维能力(许多在非软件技术型岗位上的人没有进行过形式逻辑的训练,因此在和他们工作时要特别小心,在将业务知识编撰成规则时,要特别注意业务规则和流程应当是当前能够理解的)。 如果规则对于你的项目组来说是一种新的技术,那在使用前必须将学习与管理的费用成本考虑进去。规则不是一种无意义的技术,这篇文档尽量让其易于理解。 在一个面向对象的应用中,规则引擎通常被用在包含业务逻辑的关键部分(具体与应用相关),特别是在十分繁杂凌乱的部分。这是对面向对象中将所有逻辑封装在对象中的一个倒置。但这并不是说应该抛弃对象模型,相反的来说在任何一个现实应用中业务逻辑仅仅是应用的一部分。如果你曾注意到在你的代码中有很多”if””else””switch”和其它凌乱的逻辑,你总是要回过头去修改它们(可能是由于提供给你的逻辑是错误的,或是你的理解变化了),那么可以考虑使用规则。如果你所面对的问题没有算法或者模式合适解决,考虑使用规则。 规则可以被嵌入你的应用中,或者作为一个服务使用。通常规则最好被当作一个有状态的组件使用——因此它们通常在应用中是一个整体。无论怎样,在一些规则被成功定义为可重用的服务的个案中,规则是无状态的。 如果考虑在组织中使用规则,那考虑产品中将如何使用和更新规则是很重要的(选择很多,但是不同的组织间有不同的需要——通常这超出了应用者/项目团队的控制)。 2.2.3. 何时不要使用规则 这里引用Drools邮件发送清单中的话(Dave Hamu):“在我看来,在使用规则引擎的兴奋中,人们忘记了规则引擎只是一个复杂的应用或方法中的一部分。实际上,规则引擎不是用于规则的工作流引擎或进程管理工具。对特定的工作要使用恰当的工具。当然,必要时老虎钳可以当作锤子用,但那并不是发明老虎钳的本意。” 因为规则引擎是动态的 (动态的在这里意味着规则可以象数据一样保存、管理和更新),它们通常被看作发布软件系统的一种解决方案(大多数IT部门似乎存在的目的是防止软件系统被遗弃)。如果这是你希望使用规则引擎的原因,应当意识到在可以写出公开发布的规则时,规则引擎能够以最佳方式工作。做为另一个方面,你也可以考虑使用数据驱动的设计(查找表)或者脚本/流程引擎带有能够在数据库中管理并能够动态更新的脚本。 2.2.4. 脚本或流程引擎 希望之前的讨论能够让你明白何时应该使用一个规则引擎。 另一方面,基于脚本的引擎也提供了"动态重组"的能力。 另外流程引擎 (通常为工作流)如jBPM允许你使用绘图(或编程)方式描述过程的步骤——这些步骤也能够带有包含简单规则的策略决策点。过程引擎与规则通常能够很好的在一起工作,因此这不是一个二选一的要求。 对于规则引擎的一个关键点是一些规则引擎实际上就是脚本引擎。在脚本引擎下是你的程序被牢牢绑定在脚本上(如果那里有规则,则必须直接调用),这导致未来维护更加困难,因为它们趋向于越来越复杂。在脚本引擎之上的好处是能够很容易的开始实现规则并迅速获得反馈(概念上会使得命令式编程更简单) 许多人在过去已经成功实现了数据驱动的系统(通过控制表中保存的元数据来影响应用的行为)——保持在一定控制范围内时这能够很好的工作。但是当超过了允许范围(如只有建立者能够改变应用的行为)太多这将很快失控或者导致应用停滞不前。 2.2.5. 紧密耦合与松散耦合 无疑在系统设计中你听说过紧密耦合与松散耦合。通常人们认为在设计中松散耦合是更好的,因为增加了系统灵活性。同样你可以有紧密耦合与松散耦合两种规则。紧密耦合在这里意味着一个规则的执行后将导致另一个规则有一个明显的结果,换句话说就是在规则之间有清晰的逻辑推理关系。如果你的规则都是紧密耦合的,那这些规则在将来会缺乏灵活性,并且更明显,这样规则引擎可能是使用过度了(因为逻辑是一组清晰的规则推理——能够被直接编码[一个决策树可能更适宜])。这并不是说紧密耦合或松散耦合本质就不好,但是在你考虑使用规则引擎并且捕捉规则时应当牢记这点。松散耦合使得系统中的规则在变更,删除和新增时不会影响其它无关的规则。 2.3. 知识表征 2.3.1. 一阶逻辑 规则是由一阶逻辑或断言逻辑编写而成,是由命题逻辑扩展而来。Emil Leon Post(数学家与逻辑学家 1897-1954)是第一个制定了利用符号来表示逻辑的推理系统——作为一个推论,他证明了任何逻辑系统(包括数学)能够被这样的一个符号系统来表达。 一个命题就是一项能够被归纳为True或Flase的一个声明。如果事实能够被一个独立的声明证明就称之为“封闭声明”。在程序中这表现为一个不含任何变量的表达式: 10 == 2 * 5 包含了一个或多个变量或Fact的表达式称为“开放声明”,在这种表达式中,只有当一个变量的实例被放入表达式后,才能知道该表达式是否为真值: Person.sex == "male" 在SQL语句中,一条语句的执行结果通常是将匹配的结果返回: select * from People where People.sex == "male" 所有由以上SQL返回回来的数据都会推论为是男性的数据,下图显示SQL如何作为一个推理引擎从People表中返回我们需要的结果。 Figure 2.4. SQL 作为一个单纯的推理引擎 因此在Java中,我们可以认为一个简单命题的形式是“变量”“运算符”“值”——在这里值通常代表一个字符串——命题可以看作是一个字段约束。命题之间可以使用“&&”或“||”进行逻辑合并。如下例: person.getEyeColor().equals("blue") || person.getEyeColor().equals("green") 这在规则引擎中也可以用“||”逻辑运算符来表示。 Person( eyeColour == "blue" ) || Person( eyeColor == "green" ) “||”逻辑运算也可以直接作用于字段约束上(Drools4.0M2实现该功能) Person( eyeColour == "blue"||"green" ) 命题逻辑并不能表达数据结构的标准(不具有图灵完备性),这约束了其可以定义的问题。一阶逻辑或断言逻辑通过两种新的量词概念以允许表达式定义结构——具体来说是全称量词和存在量词,从而扩展了命题逻辑。全称量词允许对每一个对象检查某些情况是否为真,通常使用“forall”这个条件元来支持(在Drools4.0M2中实现)。存在量词检查对象的存在性,这意味着该对象至少要出现一次——由“not”和“exist”这两个条件元支持。 设想我们定义了两个类——Student和Module。Module代表了一个学生在每个学期里要上的所有课程,通过List集合引用。在学期末,每个课程有一个得分。如果某个学生的所有课程中有一门低于40分代表该学生本学期不合格——通过存在量词可以使用“less then 40” 这样的开放命题来检查各Module中是否有符合该条件的情况。 public class Student { private String name; private List modules; ... } public class Module { private String name; private String studentName; private int score; Java具有图灵完备性,因此你可以通过编写代码遍历数据结构来检查存在性。下面的代码用来返回所有学期不合格的学生列表。 List failedStudents = new ArrayList(); for ( Iterator studentIter = students.iterator(); studentIter.hasNext() { Student student = ( Student ) studentIter.next(); for ( Iterator it = student.getModules.iterator(); it.hasNext(); ) { Module module = ( Module ) it.next(); if ( module.getScore() < 40 ) { failedStudents.add( student ) ; break; } } } 早期的SQL实现不是图灵完备的,因此不能提供对所存取的数据结构的量化性。目前的SQL引擎通过对嵌套SQL语句进行“exist”和“in”的关键字构造来支持存在性。以下代码显示如何通过SQL和规则来获取学期不合格的学生。 select * from Students s where exists ( select * from Modules m where m.student_name = s.name and m.score < 40 ) rule "Failed_Students" when exists( $student : Student() && Module( student == $student, score < 40 ) ) 2.4. Rete 算法 RETE算法是由Charles Forgy博士发明的,发表在他的博士论文中(1978-79)。这篇文章的简化版本发布在1982年(http://citeseer.ist.psu.edu/context/505087/0)。 单词Rete是拉丁语中的网的意思,代表网络的概念。Rete算法可以被分为两个部分:规则编辑和允许环境中的执行。 编辑算法描述了在生产空间(Production Memory)的规则如何产生一个有效的鉴别网络。用通俗的话来说,有效的鉴别网络是用来过滤数据的。办法是对在网络间传递的数据进行过滤。在网络顶部时节点将匹配许多数据,但是到了网络底部是将匹配较少的数据。在网络的很底部是终端节点。在Frogy博士1982年的文章中,他描述了四种基本的节点:root,1-input,2-input和terminal。 Figure 2.5. Rete Nodes 所有对象通过Root节点进入网络。从那之后立刻进入ObjectTypeNode节点。ObjectTypeNode节点的目的是保证引起不会做超过它需要做的工作范围。例如有两个对象:Account和Order。如果规则引擎尝试在每一个节点评估所有对象,这将花费很多循环。为了让工作更有效率,规则引擎应当只将与节点匹配的对象类型传入节点。最初是建立一个ObjectTypeNode节点,所有1-input和2-input节点都跟在后面。通过这种方法,应用传进来的Account对象不会被传送到需要Order对象的节点上。在Drools中,当一个对象被设置后,它将通过对象的类在HashMap中查找有效的ObjectTypesNode节点列表,如果这个列表不存在,它会搜索所有的ObjectTypesNode节点,找到有效的匹配并缓存到列表中。这能够让Drools通过一个instanceof检查来匹配任何对象类型。 Figure 2.6. ObjectTypeNodes ObjectTypdeNode节点能够向AlphaNode、LeftInputAdapteNode和BetaNode节点传播数据。AlphaNode节点用来对字符串进行求值。虽然在1982年的论文中只提到了等式操作,但许多Rete算法的实现支持其它操作。例如,Account.name==”Mr Trout”是一个字符串等式。当规则对一个对象类型有很多字符串等式时,它们会链接在一起。这意味着如果应用设置了一个Account对象,它必须满足了一个字符串等式后才能传递到下一个AlphaNode节点。在Forgy博士的论文中,他称之为内在条件。下面显示了针对Cheese对象的结合AlphaNode节点情况:Cheese( name == "cheddar, strength == "strong" ): Figure 2.7. AlphaNodes Drools 通过在ObjectTypdeNode 节点向AlphaNode节点传递时使用Hash操作优化了Rete算法。每次一个AlphaNode节点被加入到ObjectTypdeNode 节点时,将在HashMap中增加一个以字符串为键,AlphaNode节点为值的键值对。当一个新的对象实例进入ObjectTypdeNode节点时,它可以直接从HashMap中返回正确的AlphaNode节点,避免了多余的字符串检查。 有两种two-input节点——JoinNode和NotNode,都是BetaNode的类型。BetaNode节点用于比较两个对象和它们的字段。两个对象可能是相同或不同的类型。我们将这两个输入称为左和右。BetaNode的左输入通常是一组对象的列表,在Drools中称为语义(Tuple)。右边的输入是单个的对象。这两者能够使用“exist”进行对比检查。BetaNode具有记忆功能。左边的输入被称为Beta Memory,会记住所有到达过的语义。右边的输入成为Alpha Memory,会记住所有到达过的对象。Drools通过在BetaNode节点上建立索引扩展了Rete算法。例如,如果我们知道一个BetaNode用来对一个字符型字段进行检查,对每一个进入的对象,我们能够使用那个字符串的值进行一个Hash查找。这意味着当Fact从相反的一边进入BetaNode时,不需要再对所有Fact进行检索以发现有效链接,只要一个查找就可以返回潜在的有效候选。在任何时候,一个有效的链接是发现语义与对象的链接,它指的是一个局部匹配,并传播到下一个节点。 Figure 2.8. JoinNode 为了让最初的对象,在上图中是Cheese,进入Rete网络,我们使用了一个LeftInputNodeAdapter——将一个对象作为输入并传播一个对象的语义。 终端节点被用来表示一个单独的规则已经匹配了它的所有条件——在这一点上,我们说这个规则实现完全的匹配。一个使用“or”链接符的规则在每一个可能的逻辑路径上产生子规则,在这种情况下可以有多个终端节点。 Drools使用了节点共享技术。许多规则重复着同样的模式,节点共享允许我们删除这些模式的多余复制,从而避免对每一个单独得实例进行重复的评估。下面两个规则在一开始共享了相同的模式,但后面部分不同: rule when Cheese( $chedddar : name == "cheddar" ) $person : Person( favouriteCheese == $cheddar ) then System.out.println( $person.getName() + " likes cheddar" ); end rule when Cheese( $chedddar : name == "cheddar" ) $person : Person( favouriteCheese != $cheddar ) then System.out.println( $person.getName() + " does not like cheddar" ); end 你可以在下图看到,在编译的Rete网络中Alpha节点被共享了,但是Beta节点没有共享。每一个Beta节点有自己的终端节点。如果第二个模式是相同的也会被共享。 Figure 2.9. Node Sharing 2.5. Drools 规则引擎 2.5.1. 概述
- 粉丝: 9
- 资源: 111
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
- 1
- 2
前往页