有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java规则引擎根据多个条件过滤多个输入对象

我想设计一个规则引擎来过滤传入的对象,如下所示:

一开始,我有三个不同的类:A、B、C。类的列表是动态的,也就是说:我想扩展这个类来处理D、e等类,如果D和e以后会被添加的话

public class A {
   int a1;
   String a2;
   boolean a3;
   Date a4;
   List<String> a5;
   ...
}

public class B {
   String b1;
   boolean b2;
   Date b3;
   long b4;
   ...
}

public class C {
   String c1;
   boolean c2;
   Date c3;
   long c4;
   ...
}

将有A类、B类或C类的不同对象将被我的规则引擎过滤

用户可以根据类的每个成员变量可能具有的一组预定义操作定义不同的规则

以下是一些操作的示例:

  • a1可以有如下操作:>;=<;=,或者在某个范围内等等
  • a2可以有如下操作:is not或is等
  • a3只能是真或假
  • a4可以是:特定日期之前、特定日期之后或之间,等等
  • a5可以是:列表中存在或不存在,等等

A类对象的一些示例规则如下:

  • a1在0到100之间,a2不是“ABC”,a3为假,a4在今天之前,a5包含“cdf”,等等
  • a2是“abc”,a3是真的,a4是介于某个日期范围之间的
  • 等等

一颗子弹就是一条规则。在一条规则中,可以有一个或多个标准(多个标准的和在一起)

每个标准都由一个成员变量定义,该变量带有一个我可以应用于该变量的操作

规则引擎必须能够处理用户为A类、B类或C类的每个对象定义的规则。对于传入的每个规则(A规则、B规则或C规则),返回的将是与指定规则匹配的对象列表

我可以创建Criteria、Criteria、ARule、BRule、CRule、Operation对象等;我可以用蛮力的方式做事;但如果。。。其他的声明

我很欣赏任何设计模式/设计方法的所有想法,我可以使用这些想法来让这一点变得清晰和可扩展

非常感谢您抽出时间


共 (1) 个答案

  1. # 1 楼答案

    听起来规则实际上是一个^{},由-ing其他谓词构成。使用Java 8,用户可以为属性定义谓词:

    Predicate<A> operationA1 = a -> a.getA1() >= 10;           // a1 is int
    Predicate<A> operationA2 = a -> a.getA2().startsWith("a"); // a2 is String
    Predicate<A> operationA3 = a -> a.getA3(); // == true;        a3 is boolean
    
    Predicate<A> ruleA = operationA1.and(operationA2).and(operationA3);
    

    现在,您可以流式处理List<A>,过滤并收集到新列表:

    List<A> result = listOfA.stream()
        .filter(ruleA)
        .collect(Collectors.toList());
    

    对于BC可以使用类似的方法

    现在,有几种方法来抽象所有这些。这里有一个可能的解决方案:

    public static <T, P> Predicate<T> operation(
            Function<T, P> extractor, 
            Predicate<P> condition) {
    
        return t -> condition.test(extractor.apply(t));
    }
    

    此方法基于^{}创建一个谓词(表示您的一个操作),该谓词将从ABC(或将来的类)以及该属性上的Predicate提取属性

    对于我上面展示的相同示例,您可以这样使用:

    Predicate<A> operation1A = operation(A::getA1, p -> p >= 10);
    Predicate<A> operation2A = operation(A::getA2, p -> p.startsWith("a"));
    Predicate<A> operation3A = operation(A::getA3, p -> p); // p == true ?
    

    但是,由于该方法是通用的,您也可以将其用于B的实例:

    Predicate<B> operation1B = operation(B::getA1, p -> p.startsWith("z"));
    Predicate<B> operation2B = operation(B::getA2, p -> !p); // p == false ?
    Predicate<B> operation3B = operation(B::getA3, p -> p.before(new Date()));
    

    既然您已经定义了一些操作,那么您需要一种通用的方法来从这些操作中创建一个排除规则:

    public static <T> Predicate<T> rule(Predicate<T>... operations) {
        return Arrays.stream(operations).reduce(Predicate::and).orElse(t -> true);
    }
    

    此方法通过对给定的操作进行排序来创建规则。它首先从给定数组创建一个流,然后通过对操作应用^{}方法来减少该流。您可以查看^{}^{}^{}文档以了解详细信息

    因此,要为A创建规则,可以执行以下操作:

    Predicate<A> ruleA = rule(
        operation(A::getA1, p -> p >= 10),
        operation(A::getA2, p -> p.startsWith("a")),
        operation(A::getA3, p -> p));
    
    List<A> result = listOfA.stream()
        .filter(ruleA)
        .collect(Collectors.toList());