政府身份证ID的例子
到目前为止,我们知道了什么是推断,并且有一个基本示例,这如何促进良好的规则设计和维护?
思考一下,政府的身份证号管理部门,当儿童成年时负责签发身份证。他们可能有一个像这样包括逻辑的决策表,当在伦敦居住满18年,或者超过18年时签发身份证:
RuleTable ID Card | |||
CONDITION | CONDITION | ACTION | |
p : Person | |||
location | age >= $1 | issueIdCard($1) | |
Select Person | Select Adults | Issue ID Card | |
Issue ID Card to Adults | London | 18 | p |
但是身份证部门并没有制定谁是成年人的政策。这个政策是在政府层面完成的。如果政府将这个年龄更改为21,这将启动一个变更的管理流程。某个人必须与身份证部门联系,确保他们的系统更新,及时让法律生效。
这个更改管理流程和部门之间的通信这种情况来说并不理想,并且更改成为成本高和容易出错的事情,此外,身份证管理部门管理了更多的信息,有一些信息本身是不需要身份证管理部分管理的,这样就会更容易的泄露一些信息。意思是,系统根本就不关心年龄大于18这样明确的信息,而是只关心是否是成年人就行。
与此相反,让我们使用一种责任分离的方法,以便政府和身份证部分可以维护他们自己的规则。
决定谁是成年人是政府的工作,如果他们更改法律,他们只需要臫新规则更新他们的中间仓库:
RuleTable Age Policy | ||
CONDITION | ACTION | |
p : Person | ||
age >= $1 | insert($1) | |
Adult Age Policy | Add Adult Relation | |
Infer Adult | 18 | new IsAdult( p ) |
如之前讨论的那样,IsAdult事实是从政策规则推断来的。IsAdult封装了一条看似随意的逻辑“age>=18”,并且提供了IsAdult的抽象含义。现在,如果有部门使用上述规则,他们不再需要知道是否某人是成年人这样明确的信息,他们仅仅使用推断事实:
RuleTable ID Card | |||
CONDITION | CONDITION | ACTION | |
p : Person | isAdult | ||
location | person == $1 | issueIdCard($1) | |
Select Person | Select Adults | Issue ID Card | |
Issue ID Card to Adults | London | p | p |
虽然例子是非常小并且琐碎的,但是阐述了一些重要的点。我们从单一的,漏洞百出的只是工程方法开始。我们创建了一个单独的决策表,这个决策表包含所有的可能信息,并且从政府泄露了身份证部门不关心也不想管理的信息。
我们首先解除了支持过程中的耦合,所以每个部门只需要对他们需要知道的东西负责。然后使用推断事实IsAdult封装了这个泄露的知识。术语IsAdult的使用也对之前很随意的逻辑"age>=18"进行了抽象。
因此,在进行知识工程时,一般的经验法则是:
不好的方面
- 整体化
- 泄露
好的方面
- 知识职责解耦
- 封装知识
- 对封装提供语义抽象
Drools规则引擎中的事实相等模式
规则引擎支持下面的事实相等模式,这个相等模式决定规则引擎如何去存储和比较插入的事实:
- 标识符(默认):规则引擎用IdentityHashMap存储所有插入的事实。对于每一个新插入的事实,规则引擎会返回一个新的FactHandle对象。如果事实被再次插入,规则引擎会返回旧的FactHandle对象,,忽略对于相同事实的重复插入。在这种模式下,两个事实是否相等,只看他们是否是相同的对象并且拥有相同的标识符。
- 等于:规则引擎使用HashMap存储所有插入的事实。只有当插入的事实和所有已经存在的事实都不相等的时候,引擎才会返回一个新的FactHandle对象,是否相等需要根据插入事实的equals方法来决定。在这个模式下,对于规则引擎来说,如果两个事实以相同的方式组成,无论标识符是否相等,都是相同的事实。当你想要对象基于相等功能插入,而不是明确的标识插入,可以使用这个模式。
作为事实相等模式的说明,请考虑以下示例事实:
实例事实
Person p1 = new Person("John", 45);
Person p2 = new Person("John", 45);
在标识符模式下,事实p1和p2是Person类的不同实例,被作为不同的对象对待,因为他们有各自的标识符。在相等模式下,事实p1和p2是被作为相同对象对待的,因为他们里面的内容是一样的。这种行为上的区别,会影响你和事实处理器的交互。
举个例子,假设你在规则引擎中插入了事实p1和p2,然后你想要为p1检索事实处理器。在标识符模式下,你必须指定p1,才能精确的返回对应的事实处理。而在等于模式下,你可以指定p1,p2甚至new Person("John",45),都可以返回事实处理器。
插入事实,并在标识符模式下返回实时处理器的例子
ksession.insert(p1);
ksession.getFactHandle(p1);
插入事实,在等于模式下返回处理器的例子
ksession.insert(p1);
ksession.getFactHandle(p1);
// Alternate option:
ksession.getFactHandle(new Person("John", 45));
设置成事实相等模式,使用下面选项中的一个:
- 将系统属性drools.equalityBehavior设置成identity(默认)或者equlity
- 当写代码创建Kie base时,设置成equality模式
KieServices ks = KieServices.get();
KieBaseConfiguration kieBaseConf = ks.newKieBaseConfiguration(); kieBaseConf.setOption(EqualityBehaviorOption.EQUALITY);
KieBase kieBase = kieContainer.newKieBase(kieBaseConf);
- 在kmodule文件中的kbase标签属性中设置equality模式
<kmodule>
...
<kbase name="KBase2" default="false" equalsBehavior="equality" packages="org.domain.pkg2, org.domain.pkg3" includes="KBase1">
...
</kbase>
...
</kmodule>