传统上,要使用OptaPlanner进行扩展,您必须学习DRL。 不再。 借助受Java 8 Streams和SQL启发的新Constraints Streams API,您现在可以用Java (或Kotlin或Scala) 编写约束,并且仍然可以从增量计算中受益。
在下面,约束流(CS)仍使用强大的Drools引擎。 我们也仍然完全支持得分DRL。 它们不被弃用。
让我们从一个例子开始。 在护士排班中,为了避免将班次分配给员工Ann
,您可以在DRL中编写以下约束:
rule "Don't assign Ann" when Shift(getEmployee().getName() == "Ann" ) then scoreHolder.addSoftConstraintMatch(kcontext, - 1 ); end
这在使用约束流的Java中是相同的约束:
Constraint constraint = constraintFactory .from(Shift. class ) .filter(shift -> shift.getEmployee().getName().equals( "Ann" )) .penalize( "Don't assign Ann" , HardSoftScore.ONE_SOFT);
如果您熟悉SQL或Java 8流,则应该看起来很熟悉。 给定一个有四个班次的潜在解决方案(其中两个分配给Ann
),这些班次将通过约束流流动,如下所示:
这种写约束的新方法有几个好处:
增量计算
首先,与EasyScoreCalculator
不同,约束流仍然像DRL一样应用增量分数计算来进行横向扩展。 例如,当一个移动将雇员换两班时,仅计算增量。 这是巨大的可扩展性收益:
索引编制
当JOIN
多个类型时,就像SQL JOIN
运算符一样,约束流在索引上应用哈希查找以更好地扩展:
IDE支持
因为ConstraintsStreams是用Java语言编写的,所以它们背负了非常强大的工具支持。
代码突出显示,代码完成和调试工作正常:
代码突出显示
IntelliJ IDEA Ultimate中的DRL代码:
对于相同的约束,在IntelliJ IDEA Ultimate中使用约束流的Java代码:
代码完成
约束流的代码完成:
当然,所有API方法都具有Javadocs。
调试
在ConstraintStream的filter()
添加一个断点:
在调试时诊断问题:
Java语法
用约束流用Java编写的约束,无论好坏,都遵循Java语言规范(JLS)。 当使用来自Kotlin或Scala的约束流时,适用类似的逻辑。
在DRL和约束流之间迁移时,请注意DRL和Java之间的一些区别:
- DRL的
==
运算符在Java中转换为equals()
。 - 除了getter,DRL还允许MVEL表达式转换为Java中的getter。
例如,此DRL具有name
和==
:
rule "Don't assign Ann" when Employee(name == "Ann" ) then ... end
但是,对于完全相同的约束,Java变量具有getName()
和equals()
:
constraintFactory.from(Employee. class ) .filter(employee -> employee.getName().equals( "Ann" )) .penalize( "Don't assign Ann" , ...);
进阶功能
Constraint Streams API使我们可以添加语法糖和强大的新概念,这些概念专门为帮助您构建复杂的约束而量身定制。
为了突出其中之一,让我们看一下功能强大的groupBy
方法:
与SQL GROUP BY
运算符或Java 8 Stream Collector
相似,它支持sum()
, count()
, countDistinct()
, min()
, max()
, toList()
甚至自定义函数,同样也不会损失增量分数计算。
约束流的未来工作
首先,非常感谢LukášPetrovický在Constraints Streams上所做的所有工作!
但这仅仅是开始。 我们设想了更高级的功能,例如负载平衡/公平方法,以使此类约束更易于实现。
目前,我们的首要任务是简化对隔离的单元测试的单元。 考虑测试驱动设计。 敬请关注!
翻译自: https://www.javacodegeeks.com/2020/04/constraint-streams-modern-java-constraints-without-the-drools-rule-language.html