Optaplanner/Timefold:如何动态获取初始化的SolutionCloner避免硬编码?
动态获取Optaplanner/Timefold的SolutionCloner
我有一个可正常运行的Optaplanner/Timefold解决方案,能为业务提供日间实时反馈。但变更请求会临时排队,因此我需要克隆当前最优解决方案,在将请求提交到主方案前测试其是否存在可行解。
目前我通过以下代码克隆解决方案:
SolutionDescriptor solutionDescriptor = SolutionDescriptor.buildSolutionDescriptor(DistributionSolution.class, MoverEntity.class, LoaderEntity.class ); FieldAccessingSolutionCloner<DistributionSolution> distSolCloner = new FieldAccessingSolutionCloner<>(solutionDescriptor); DistributionSolution clonedSolution = distSolCloner.cloneSolution(currentDistributionSolution);
但硬编码解决方案和实体类的方式不够健壮,虽然可使用solverConfig.xml,但这仍是另一种硬编码,也可移至工厂类等。不过我希望找到更动态的方式获取初始化后的SolutionCloner。我注意到TimeFoldProcessor.class似乎通过反射实现了该功能,但不确定如何在运行时访问它?请问有什么解决思路吗?
解决思路
1. 从已初始化的Solver实例中提取SolutionDescriptor
如果你已经有运行中的Solver或SolverFactory实例,直接从中获取SolutionDescriptor是最直接的方案,完全复用已有配置,避免重复硬编码:
// 假设已初始化好SolverFactory SolverFactory<DistributionSolution> solverFactory = ...; SolutionDescriptor<DistributionSolution> solutionDescriptor = solverFactory.getSolverConfig().getSolutionDescriptor(); FieldAccessingSolutionCloner<DistributionSolution> cloner = new FieldAccessingSolutionCloner<>(solutionDescriptor); DistributionSolution clonedSolution = cloner.cloneSolution(currentDistributionSolution);
2. 用反射工具类自动识别实体类
Timefold内置的ReflectionSolutionDescriptorFactory可以通过反射自动扫描解决方案类中的@PlanningEntityCollectionProperty注解,自动识别所有关联的实体类,无需显式指定:
// 仅传入解决方案类,自动解析实体类型 SolutionDescriptor<DistributionSolution> solutionDescriptor = ReflectionSolutionDescriptorFactory.buildSolutionDescriptor(DistributionSolution.class); FieldAccessingSolutionCloner<DistributionSolution> cloner = new FieldAccessingSolutionCloner<>(solutionDescriptor); DistributionSolution clonedSolution = cloner.cloneSolution(currentDistributionSolution);
3. 封装通用克隆工具类
如果需要在多处复用,可封装一个支持任意解决方案类型的工具类:
public class SolutionClonerUtil { @SuppressWarnings("unchecked") public static <T> T cloneSolution(T solution) { Class<T> solutionClass = (Class<T>) solution.getClass(); SolutionDescriptor<T> solutionDescriptor = ReflectionSolutionDescriptorFactory.buildSolutionDescriptor(solutionClass); FieldAccessingSolutionCloner<T> cloner = new FieldAccessingSolutionCloner<>(solutionDescriptor); return cloner.cloneSolution(solution); } }
使用时直接传入实例即可:
DistributionSolution clonedSolution = SolutionClonerUtil.cloneSolution(currentDistributionSolution);
关于TimeFoldProcessor的说明
TimeFoldProcessor是编译期注解处理器,作用是在编译时生成优化的克隆器、移动器等代码,而非运行时反射。如果想使用它生成的高效克隆器:
- 确保项目引入Timefold注解处理器依赖
- 解决方案类添加
@PlanningSolution注解,实体类添加@PlanningEntity注解 - 编译后会自动生成
{你的解决方案类名}Cloner类,运行时直接实例化使用:
// 编译生成的克隆器,性能比FieldAccessingSolutionCloner更高 DistributionSolutionCloner cloner = new DistributionSolutionCloner(); DistributionSolution clonedSolution = cloner.cloneSolution(currentDistributionSolution);
这种方式既避免硬编码,又能获得更优的克隆性能。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

