规则引擎

我应该使用规则引擎吗?

规则引擎旨在提供一种替代的计算模型。与通常的命令式模型(由一系列包含条件语句和循环的命令组成)不同,规则引擎基于产生式规则系统。产生式规则系统是一组产生式规则,每条规则都包含一个条件和一个动作——简单来说,你可以把它想象成一系列“如果……那么……”语句。

巧妙之处在于规则可以以任意顺序编写,引擎会根据自身情况决定何时执行这些规则。一种比较好的理解方式是,系统会遍历所有规则,筛选出满足条件的规则,然后执行相应的操作。这种方法的优点在于,许多问题都天然符合这种模型:

1
2
3
4
  if car.owner.hasCellPhone then premium += 100;
  if car.model.theftRating > 4 then premium += 200;
  if car.owner.livesInDodgyArea && car.model.theftRating > 2 
     then premium += 300;

规则引擎是一种工具,它简化了使用这种计算模型进行编程的过程。它可以是一个完整的开发环境,也可以是一个能够与传统平台兼容的框架。近年来,我看到的大多数规则引擎都是旨在与现有平台集成的工具。曾经有人设想使用这样的工具构建整个系统,但现在人们(明智地)倾向于仅将规则引擎用于系统的特定部分。产生式规则计算模型最适合处理一部分计算问题,因此规则引擎更适合嵌入到大型系统中。

你可以自己构建一个简单的规则引擎。你只需要创建一系列包含条件和动作的对象,将它们存储在一个集合中,然后遍历这些对象来评估条件并执行动作。但通常情况下,人们所说的“规则引擎”指的是专门用于帮助你构建和运行规则引擎的产品。指定规则的技术多种多样,例如,可以使用 API 将规则描述为 Java 对象,可以使用 DSL 来表达规则,或者使用 GUI 来输入规则。更高效的执行引擎可以使用专门的算法(例如 Rete 算法)快速评估数百条规则的条件。

规则引擎的一个重要特性是链式调用 ——即一条规则的动作部分会改变系统的状态,进而影响其他规则的条件部分的值。链式调用听起来很有吸引力,因为它支持更复杂的行为,但很容易导致逻辑混乱和调试困难。

我遇到过一些使用规则引擎产品的案例,但每次结果似乎都不尽如人意(声明:我的样本并不具有统计学意义)。规则引擎的核心卖点通常是允许业务人员自行定义规则,从而无需程序员的参与。这听起来似乎很有道理,但实践中却鲜有成功案例。

即便如此, BusinessReadableDSL 仍然有其价值,事实上,我确实认为这种计算模型在这方面很有价值。但这里也暗藏玄机。最大的问题在于,虽然浏览规则列表并发现每条规则都合理可行,但规则之间的交互往往非常复杂——尤其是在使用链式调用时。因此,我经常听到这样的抱怨:规则系统搭建起来很容易,但维护起来却非常困难,因为没有人能够理解这种隐式的程序流程。这就是放弃命令式计算模型的弊端。尽管命令式代码存在诸多缺陷,但它的工作原理相对容易理解。而对于生产型规则系统来说,很容易出现这样的情况:一个地方的简单改动就会导致许多意想不到的后果,而这些后果往往难以避免。

我还没有花足够的时间研究这些系统,所以还无法了解我们应该遵循哪些启发式方法来控制这种隐性行为。

  • 限制规则的数量似乎很重要,事实上,任何规则多到需要复杂的算法才能获得良好性能的系统,其规则数量可能过多,难以理解。
  • 使用链式调用时务必谨慎,通常最好组织好规则以限制甚至消除链式调用。
  • 与许多地方一样,这里的测试常常被低估,但隐式行为使得测试变得更加重要——而且需要使用生产数据进行测试。
  • 在构建规则系统时,我会考虑做一些事情,这些事情会导致规则库的修改出现早期问题

所有这些都让我觉得,避免使用规则引擎产品有很多好处。生产规则的基本理念非常简单。为了控制隐式行为,还需要通过将规则限制在特定的上下文中来减少规则的数量。这说明应该采用更领域特定的规则方法,即团队构建一个功能有限的规则引擎,该引擎仅设计用于特定的上下文。当然,如果您正在考虑使用规则引擎,我建议您同时使用产品和自行开发的领域特定方法进行原型设计,以便更好地了解它们之间的差异。

有关构建自己的简单规则引擎的更多信息,包括一些玩具示例,请参阅我的 DSL 书中“生产规则系统” 章节。

参考

本文翻译于 RulesEngine

Licensed under CC BY-NC-SA 4.0
本文阅读量 次, 总访问量 ,总访客数
Built with Hugo .   Theme Stack designed by Jimmy