如何更改默认的 ConflictResolver

How to change default ConflictResolver

如标题所述,我想更改我的 Drools 项目中的冲突解决程序。我在 site

上找到了以下片段
ConflictResolver[] conflictResolvers = 
new ConflictResolver[] { SalienceConflictResolver.getInstance( ),
                         FifoConflictResolver.getInstance( ) };

RuleBase ruleBase = java.io.RuleBaseLoader( url, CompositeConflitResolver( conflictResolver));

但是它缺少有关将其放置在何处以及 url 参数应该是什么的信息。

提前感谢您的帮助。

如文档所述:

Drools 4.0 supported custom conflict resolution strategies; while this capability still exists in Drools it has not yet been exposed to the end user via knowledge-api in Drools 5.0. http://docs.jboss.org/drools/release/5.2.0.Final/drools-expert-docs/html/ch04.html

因此,如果您使用的是 Drools 5+,您将无法更改冲突解决程序 ,除非您使用反射 魔法。 Conflict Resolver 安置在StatefulKnowledgeSession 对象的Agenda 对象中。你可以使用调试器看到这个(它是 Agenda 对象的内容):

要替换 ConflictResolver,首先您需要 StatefulKnowledgeSession 的实例(在以下代码段中为 ksession)。然后你需要提取一些嵌套的私有字段,然后你可以用 RandomConflictResolver 的实例替换字段值。完整代码:

        Agenda agenda = ksession.getAgenda();
        Field agendaField = agenda.getClass().getDeclaredField("agenda");
        agendaField.setAccessible(true);
        Object agendaObject = agendaField.get(agenda);


        Field mainField = agendaObject.getClass().getDeclaredField("main");
        mainField.setAccessible(true);
        Object mainObject = mainField.get(agendaObject);

        Field queueField = mainObject.getClass().getDeclaredField("queue");
        queueField.setAccessible(true);
        Object queueObject = queueField.get(mainObject);

        Field comparatorField = queueObject.getClass().getDeclaredField("comparator");
        comparatorField.setAccessible(true);
        Object comparator = comparatorField.get(queueObject);

        ConflictResolver randomResolver = org.drools.conflict.RandomConflictResolver.getInstance();
        comparatorField.set(queueObject, randomResolver);

基于:documentation 和调试器会话。

为了证明触发顺序不会影响整体结果,我会使用 AgendaFilter。我正在使用 6.x 绘制轮廓,但是这个 API 自 5.x.

以来就没有改变过
KieBase kieBase = ...;
Collection<KiePackage> packages = kieBase.getKiePackages();
List<Rule> rules = new ArrayList<>();
for( KiePackage p: packages ){
    rules.addAll( p.getRules() );
}

现在你已经掌握了所有的规则。构建这个接受单个规则的简单过滤器:

class Selector implements AgendaFilter {
    List<Rule> rules;
    int ruleIndex;
    Selector( List<Rule> rules ){
        this.rules = rules;
    }
    void setRuleIndex( int value ){ this.ruleIndex = value; }
    int getRulesSize(){ return rules.size(); }
    boolean accept(Match match){
        return match.getRule().equals( rules.get( ruleIndex ) );
    }
}

实例化:

Selector selector = newSelector( rules );

您现在可以执行所有已激活的规则(但请参阅下文):

for( int i = 0; i < selector.getRulesSize(); ++i ){
    int fired = kieSession.fireAllRules( selector, i );
}

0..size-1 的任何其他排列都可能产生另一个触发序列。您可以针对少量规则系统地执行此操作,或者您可以使用一些随机排列。

更有效的测试会跟踪在第一个 运行 中传递给过滤器的匹配数据,并仅将这些数据用于后续执行。

注意 目前概述的方法没有考虑工作记忆中的变化。当某些规则 n+k 被触发时,规则 n 可能会被激活。如果你确实改变了工作记忆,你将不得不

do {
    int sumf = 0;
    for( int i : somePermutation ){
        int fired = kieSession.fireAllRules( selector, i );
        sumf += fired;
    }
} while( sumf > 0 );

我从来没有做过这样的测试。似乎通过依赖于规则触发的先天顺序来获得正确的结果是极其罕见的,与从这个顺序获得各种错误的结果相比。

注意 通过更改 DRL(或它们的包中?)中的规则顺序或将事实插入的顺序更改为工作记忆。对于某些场景,即使这样也可以提供足够的测试用例来展示您的意图。