You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:20:04