我想设计一个规则引擎来过滤传入的对象,如下所示:
一开始,我有三个不同的类: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 可以进行如下操作:不是、是等。
- a3 只能为 true 或 false。
- a4 可以是:特定日期之前、特定日期之后或之间等。
- a5可以是:列表中存在或不存在等
A 类对象的一些示例规则如下:
- a1 在 0 到 100 之间,a2 不是“ABC”,a3 为 false,a4 是在今天之前,a5 包含“cdf”等。
- a2 为“abc”,a3 为 true,a4 位于某个日期范围内。
- 等等
一颗子弹就是一条规则。一条规则中可以有一个条件,也可以有多个条件(多个条件通过 AND 组合在一起)。
每个标准都是由一个成员变量定义的,我可以在该变量上应用一个操作。
规则引擎必须能够处理用户为 A、B 或 C 类的每个对象定义的规则。对于传入的每个规则(A 规则、B 规则或 C 规则),返回将是一个列表与指定规则匹配的对象。
我可以创建 Criterion、Criteria、ARule、BRule、CRule、Operation 对象等;我可以用蛮力的方式做事;但这将会有很多 if...else... 语句。
我很欣赏任何设计模式/设计方法的所有想法,我可以使用它们来使其干净且可扩展。
非常感谢您抽出时间。
最佳答案
听起来规则实际上是 Predicate
由和-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());
您可以对 B
使用类似的方法和C
.
现在,有几种方法可以抽象这一切。这是一种可能的解决方案:
public static <T, P> Predicate<T> operation(
Function<T, P> extractor,
Predicate<P> condition) {
return t -> condition.test(extractor.apply(t));
}
此方法基于 Function
创建一个谓词(代表您的操作之一)将从 A
中提取属性, B
或C
(或 future 的类(class))和 Predicate
超过该属性(property)。
对于我上面展示的相同示例,您可以这样使用它:
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);
}
此方法通过和对给定操作进行操作来创建规则。它首先从给定数组创建一个流,然后通过应用 Predicate#and
来减少该流。方法来进行操作。您可以查看 Arrays#stream
, Stream#reduce
和 Optional#orElse
文档了解详细信息。
因此,为 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());
关于java - 规则引擎根据多个条件过滤多个输入对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44764849/