从Drools迁移至Constraint Streams:动态约束级别配置求助
解决Constraint Streams动态约束级别与权重的方案
针对你从Drools迁移到Constraint Streams时遇到的动态约束配置问题,这里提供两种简洁的实现方案,避免冗余代码:
方案一:通过约束配置实体动态计算惩罚分数
这种方式直接将约束的级别、权重作为问题事实(Problem Fact)加入规划模型,在约束流中关联实体与配置,动态生成对应级别的惩罚分数。
步骤1:定义约束配置类
public class ConstraintConfig { private String constraintType; // 约束类型标识,用于匹配对应的业务逻辑 private ConstraintLevel level; private int weight; private int maxAllowedOvertime; // 示例业务参数,可根据实际场景调整 // 构造器、getter、setter }
步骤2:编写约束流逻辑
在ConstraintProvider中,通过join关联业务实体与约束配置,根据配置动态生成惩罚分数:
public class MyConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { return new Constraint[] { dynamicOvertimeConstraint(constraintFactory) }; } private Constraint dynamicOvertimeConstraint(ConstraintFactory cf) { return cf.from(Employee.class) // 关联对应的约束配置,匹配约束类型 .join(ConstraintConfig.class, Joiners.equal(Employee::getDeptType, ConstraintConfig::getConstraintType)) // 过滤符合约束违规条件的实体 .filter((employee, config) -> employee.getOvertimeHours() > config.getMaxAllowedOvertime()) // 根据配置的级别和权重返回对应Score .penalize((employee, config) -> switch(config.getLevel()) { case HARD -> HardMediumSoftScore.ofHard(-config.getWeight()); case MEDIUM -> HardMediumSoftScore.ofMedium(-config.getWeight()); case SOFT -> HardMediumSoftScore.ofSoft(-config.getWeight()); }) .asConstraint("Dynamic Overtime Constraint"); } }
步骤3:动态更新配置
当UI修改约束的级别、权重或业务参数时,直接更新ConstraintConfig实例(比如从数据库读取或通过接口传入),重新构建问题并触发求解即可。
方案二:使用penalizeConfigurable结合ConstraintConfiguration
这种方式通过ConstraintConfiguration管理每个约束实例的权重与级别,适合需要为同一逻辑创建多个独立约束实例的场景。
步骤1:定义约束配置映射
创建ConstraintConfiguration子类,管理每个约束ID对应的权重配置:
public class CustomConstraintConfiguration extends ConstraintConfiguration { private Map<String, ConstraintWeightConfiguration> constraintWeightMap = new HashMap<>(); // 更新指定约束的级别与权重 public void updateConstraint(String constraintId, ConstraintLevel level, int weight) { Score<?> score = switch(level) { case HARD -> HardMediumSoftScore.ofHard(-weight); case MEDIUM -> HardMediumSoftScore.ofMedium(-weight); case SOFT -> HardMediumSoftScore.ofSoft(-weight); }; constraintWeightMap.put(constraintId, new ConstraintWeightConfiguration(constraintId, score)); } @Override public ConstraintWeightConfiguration getConstraintWeight(String constraintPackage, String constraintName) { return constraintWeightMap.get(constraintName); } }
步骤2:生成动态约束实例
在ConstraintProvider中,遍历所有约束配置,为每个配置生成共享同一逻辑的约束实例:
public class MyConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { List<Constraint> constraints = new ArrayList<>(); // 从外部获取所有动态约束配置(比如UI传入的配置列表) List<ConstraintConfig> configs = getDynamicConfigs(); for (ConstraintConfig config : configs) { Constraint constraint = constraintFactory.from(Employee.class) .filter(employee -> employee.getOvertimeHours() > config.getMaxAllowedOvertime()) .penalizeConfigurable(config.getConstraintId(), constraintFactory.getScoreDefinition()) .asConstraint(config.getConstraintId()); constraints.add(constraint); } return constraints.toArray(new Constraint[0]); } private List<ConstraintConfig> getDynamicConfigs() { // 示例:返回UI配置的约束列表 List<ConstraintConfig> configs = new ArrayList<>(); configs.add(new ConstraintConfig("deptA-overtime", ConstraintLevel.HARD, 10, 10)); configs.add(new ConstraintConfig("deptB-overtime", ConstraintLevel.SOFT, 5, 15)); return configs; } }
步骤3:关联配置与求解器
在求解器配置中指定自定义的ConstraintConfiguration,UI更新时直接修改配置类并重启求解器:
SolverFactory<EmployeeRoster> solverFactory = SolverFactory.create(new SolverConfig() .withSolutionClass(EmployeeRoster.class) .withEntityClasses(Employee.class) .withConstraintProviderClass(MyConstraintProvider.class) .withConstraintConfigurationClass(CustomConstraintConfiguration.class)); // UI更新配置时执行 CustomConstraintConfiguration config = solverFactory.getSolverConfig().getConstraintConfiguration(); config.updateConstraint("deptA-overtime", ConstraintLevel.MEDIUM, 8); Solver<EmployeeRoster> solver = solverFactory.buildSolver(); // 执行求解逻辑
两种方案中,方案一更适合同一约束逻辑对应多组配置的场景,代码更简洁;方案二更适合需要独立管理每个约束实例的场景,灵活性更高。
内容的提问来源于stack exchange,提问作者prem_dev
相关产品推荐
相关产品推荐

