6

我一直在研究如何简化一些用 DRL 手动编写的规则,变得难以维护。

通过谷歌搜索导致“决策表是前进的最佳方式”。

但不幸的是,我们的事实非常复杂,所以现在流口水的电子表格转换器,无法处理如此复杂的事实,

那么第一个问题是开发人员通常如何处理drools 知识库中非常复杂的事实呢?

例如,我们有类似的事实

Person->List<Cars>->List<Insurances>->Each insurance Has List<History>

现在我必须写一条规则,说这个人的保险索赔历史不好。然后我发现很难将其放入电子表格中,因为在 drl 文件上手动编写此规则更容易。

谢谢您的帮助。对上述示例的任何帮助也会非常好。

4

2 回答 2

4

对于像这样的复杂规则,我们使用 Drools 模板。您为要填充的字段编写一个带有参数扩展的规则模板,并且您可以更灵活地选择实际值的来源来填充骨架规则。

此功能内置于 Drools Guvnor,但通过 GUI 编写复杂的规则模板有些繁琐。我还编写了独立的 Java 来从从属性文件中提取的值列表中填充模板 drl 文件,并且最近开发了一个 SmartGWT Web 应用程序,它允许用户填充规则值并生成 DRL。

编辑:添加示例程序。DroolsTemplateBuilder 创建一个 TestType 对象列表,其中包含映射到 Test.drl 中模板键的字段。生成的 DRL 被打印并编译为一个 pkg,该 pkg 被写入名为 Test.pkg 的文件中。

库:antlr-3.3.jar、antlr-runtime-3.3.jar、drools-compiler-5.2.0.Final.jar、drools-core-5.2.0.Final.jar、drools-templates-5.2.0.Final。 jar、ecj-4.2.jar、knowledge-api-5.2.0.Final.jar、mvel2-2.1.0.drools.jar(这些可能并非都是必需的)。

注意:此示例使用 5.2.0 库,并且某些功能在较新版本中可能有所不同。build.xml 应该清楚地说明如何构建您的项目。

DroolsTemplateBuilder.java:

package some.test.pkg;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;

import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.common.DroolsObjectOutputStream;
import org.drools.definitions.impl.KnowledgePackageImp;
import org.drools.io.ResourceFactory;
import org.drools.template.ObjectDataCompiler;

public class DroolsTemplateBuilder {

    private String filePath;
    private String drl;

    public static void main(String[] args) {
        DroolsTemplateBuilder test = new DroolsTemplateBuilder();
        test.filePath = args[0] + File.separator + "Test.drl";
        test.runTest();
    }

    public void runTest() {
        buildPackage();
        writeRulePackageToFile();
    }

    public void buildPackage() {

        Collection<Object> templateList = new ArrayList<Object>();
        templateList.add(new TestType(1, "John", "Manager"));
        templateList.add(new TestType(2, "Peter", "CEO"));
        templateList.add(new TestType(3, "Kate", "Engineer"));

        try {
            ObjectDataCompiler converter = new ObjectDataCompiler();
            InputStream templateStream = new FileInputStream(filePath);

            String myDrl = inputStreamToString(templateStream, 200);
            // I use this ##### replacement instead of just a newline in the
            // template
            // because of windows/linux issues with newline and carriage return.
            // Drools template
            // builder, at least in 5.2.0, was very picky about the template
            // structure, including
            // where newlines are expected.
            myDrl = myDrl.replaceAll("#####", "\n");

            InputStream tempStream = new ByteArrayInputStream(myDrl.getBytes());

            drl = converter.compile(templateList, tempStream);
            System.out.println(drl);
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
        }

    }

    public void writeRulePackageToFile() {
        try {
            KnowledgeBuilder kBuilder = KnowledgeBuilderFactory
                    .newKnowledgeBuilder();

            Reader rdr = new StringReader(drl);

            kBuilder.add(ResourceFactory.newReaderResource(rdr),
                    ResourceType.DRL);

            if (kBuilder.hasErrors()) {
                System.out.println("Drools blew up on");
                for (KnowledgeBuilderError err : kBuilder.getErrors()) {
                    System.out.println(err.getMessage());
                }
            } else {
                String outFile = filePath.replaceFirst("\\.drl", ".pkg");
                OutputStream os = new FileOutputStream(outFile);
                ObjectOutputStream oos = new DroolsObjectOutputStream(os);
                KnowledgePackageImp kPackage = (KnowledgePackageImp) kBuilder
                        .getKnowledgePackages().iterator().next();
                oos.writeObject(kPackage);
                oos.close();
            }
        } catch (Exception e) {
            System.out.println("Exception " + e.getMessage());
        }
    }

    public String inputStreamToString(final InputStream is, final int bufferSize) {
        final char[] buffer = new char[bufferSize];
        final StringBuilder out = new StringBuilder();
        try {
            final Reader in = new InputStreamReader(is, "UTF-8");
            try {
                for (;;) {
                    int rsz = in.read(buffer, 0, buffer.length);
                    if (rsz < 0)
                        break;
                    out.append(buffer, 0, rsz);
                }
            } finally {
                in.close();
            }
        } catch (Exception ex) {
            System.out.println("Something went wrong: " + ex.getMessage());
        }
        return out.toString();
    }

}

测试.drl:

template header
id
name
title
#####
package some.test.pkg;

template "sampleTemplate"

rule "id filter_@{row.rowNumber}"
no-loop true
dialect "java"
when
    $t : TestType(id=="@{id}")
then
    System.out.println("Doing something special...");
end

end template

template "anotherSample"

rule "another rule_@{row.rowNumber}"
no-loop true
dialect "java"
when
    $t : TestType((name=="@{name}") || (title=="@{title}"))
then
    System.out.println("Doing something else...");
end

end template

测试类型.java:

package some.test.pkg;

public class TestType {

    private int id;
    private String name;
    private String title;

    public TestType(int id, String name, String title) {
        this.id = id;
        this.name = name;
        this.title = title;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

构建.xml:

<project name="TemplateTest" basedir="." default="jar">

    <property name="src.dir" value="src" />

    <property name="build.dir" value="build" />
    <property name="drl.dir" value="${basedir}/drl" />
    <property name="classes.dir" value="${build.dir}/classes" />
    <property name="jar.dir" value="${build.dir}/jar" />
    <property name="lib.dir" value="${basedir}/lib" />

    <path id="compile.classpath">
        <fileset dir="${lib.dir}" includes="*.jar" />
    </path>

    <path id="run.classpath">
        <fileset dir="${jar.dir}" includes="*.jar" />
        <fileset dir="${lib.dir}" includes="*.jar" />
    </path>

    <target name="clean">
        <delete dir="${classes.dir}" />
        <delete dir="${jar.dir}" />
    </target>

    <target name="compile" depends="clean">
        <mkdir dir="${classes.dir}" />
        <mkdir dir="${jar.dir}" />
        <javac includeantruntime="false" srcdir="${src.dir}" classpathref="compile.classpath" destdir="${classes.dir}" />
    </target>

    <target name="jar" depends="compile">
        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
        </jar>
    </target>

    <target name="run" depends="jar" description="run">
        <java classpathref="run.classpath" classname="some.test.pkg.DroolsTemplateBuilder" fork="true">
            <arg value="${drl.dir}" />
        </java>
    </target>

</project>
于 2013-07-24T17:58:03.493 回答
0

我们还使用模板,我们的事实(和结果规则)非常复杂。模板表中的值用于方法调用的规则、设置规则“选项”、计时器值等。

当规则参数适合表格格式时,模板会有所帮助。如果关注访问控制,您可能最终需要多个具有相同逻辑的模板,只是不同的值。(只需复制第一个模板即可在 guvnor 中轻松完成)。

于 2013-07-25T15:29:26.360 回答