Timefold:如何在PlanningEntity类中访问ProblemFact配合@CascadingUpdateShadowVariable
在Timefold中通过@CascadingUpdateShadowVariable访问PlanningSolution中的ProblemFact
方案1:通过关联实体间接访问(最简洁)
如果WorkCalendar与你的Employee实体存在关联关系(比如每个员工对应专属排班日历),可以直接在Employee类中添加WorkCalendar的引用字段。你的PlanningEntity(比如Task)通过@PlanningVariable拿到分配的Employee后,就能直接通过employee.getWorkCalendar()获取对应日历数据,无需直接访问PlanningSolution。
示例代码:
// Employee实体类 public class Employee { private WorkCalendar workCalendar; // getter/setter } // Task PlanningEntity类 @PlanningEntity public class Task { @PlanningVariable(valueRangeProviderRefs = "employeeRange") private Employee employee; @CascadingUpdateShadowVariable(sourceVariableName = "employee") private LocalDate scheduledDate; // 示例级联变量 // 通过关联的Employee获取WorkCalendar public WorkCalendar getWorkCalendar() { return employee != null ? employee.getWorkCalendar() : null; } }
方案2:用@CustomShadowVariable结合VariableListener(适配复杂场景)
如果WorkCalendar无法通过关联实体直接访问,可以结合@CustomShadowVariable和VariableListener实现。VariableListener的方法参数提供了ScoreDirector,通过它能直接获取当前PlanningSolution实例,进而拿到WorkCalendar。
示例代码:
// Task PlanningEntity类 @PlanningEntity public class Task { @PlanningVariable(valueRangeProviderRefs = "employeeRange") private Employee employee; @CustomShadowVariable(variableListenerClass = WorkCalendarShadowVariableListener.class, sourceVariableName = "employee") private WorkCalendar workCalendar; // 用影子变量存储日历 // getter/setter } // VariableListener实现类 public class WorkCalendarShadowVariableListener implements VariableListener<Task> { @Override public void afterVariableChanged(ScoreDirector scoreDirector, Task task) { // 获取当前的PlanningSolution实例 YourSolution solution = (YourSolution) scoreDirector.getWorkingSolution(); // 根据业务逻辑获取对应WorkCalendar WorkCalendar workCalendar = solution.getWorkCalendarForEmployee(task.getEmployee()); scoreDirector.beforeVariableChanged(task, "workCalendar"); task.setWorkCalendar(workCalendar); scoreDirector.afterVariableChanged(task, "workCalendar"); } // 实现其他必要接口方法(beforeVariableChanged、beforeEntityAdded等) }
关于克隆的注意事项
Timefold求解过程中会对PlanningSolution做深度克隆,用于工作内存操作。因此你的PlanningSolution类必须正确实现Cloneable接口,确保WorkCalendar这类ProblemFact被正确克隆:
- 若
WorkCalendar是不可变类,浅克隆即可; - 若为可变类,需实现深克隆,避免多个克隆实例共享同一可变对象导致数据混乱。
示例克隆实现:
public class YourSolution implements PlanningSolution, Cloneable { private List<WorkCalendar> workCalendarList; @Override public YourSolution clone() { try { YourSolution cloned = (YourSolution) super.clone(); // 深克隆WorkCalendar列表(假设WorkCalendar也实现了clone) cloned.workCalendarList = workCalendarList.stream() .map(WorkCalendar::clone) .collect(Collectors.toList()); return cloned; } catch (CloneNotSupportedException e) { throw new RuntimeException("克隆失败", e); } } }
内容的提问来源于stack exchange,提问作者Vy Le
相关产品推荐
相关产品推荐

