OptaPlanner如何使用同一模型类实现两类排课规划问题差异化配置
你的需求完全可以实现,OptaPlanner原生支持基于XML的域映射配置,可以完全替代Java注解,为两个独立规划场景分别定义不同的求解器配置即可。
方案1:使用XML域映射配置(和你设想的方案一致)
OptaPlanner的求解器配置文件支持<domainAccessType>配置为REFLECTION,然后通过<solutionDescriptor>和<entityDescriptor>标签手动定义规划解、规划实体、规划变量等映射规则,完全不需要在Java类上加注解。
第一步:保留无注解的纯净基础模型
不需要在基础类上加任何OptaPlanner相关注解,保持如下定义即可:
class Schedule { List<Teacher> teachers; List<CourseRound> courseRounds; List<Lecture> lectures; List<StudentGroup> studentGroups; List<Timeslot> timeslots; List<Room> rooms; // getter、setter 省略 } class CourseRound { Teacher teacher; String course; // getter、setter 省略 } class Lecture { Timeslot timeslot; Room room; // getter、setter 省略 }
第二步:编写教学分配场景的独立求解器配置
新建配置文件teacherAssignmentSolverConfig.xml,定义该场景下的规划映射规则:
<?xml version="1.0" encoding="UTF-8"?> <solver-config xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd"> <!-- 配置为反射访问域,不依赖注解 --> <domainAccessType>REFLECTION</domainAccessType> <!-- 定义规划解Schedule的映射规则 --> <solutionDescriptor class="替换为你项目Schedule的全类名"> <!-- 定义教师可选值范围 --> <valueRangeProvider id="teacherRange" field="teachers"/> <!-- 定义问题事实属性 --> <problemFactCollectionProperty field="studentGroups"/> <problemFactCollectionProperty field="lectures"/> <problemFactCollectionProperty field="timeslots"/> <problemFactCollectionProperty field="rooms"/> <!-- 定义规划实体集合 --> <planningEntityCollectionProperty field="courseRounds"/> <!-- 此处补充你定义的@PlanningScore对应字段的配置 --> </solutionDescriptor> <!-- 定义规划实体CourseRound的映射规则 --> <entityDescriptor class="替换为你项目CourseRound的全类名"> <!-- 定义规划变量teacher,绑定对应值范围 --> <planningVariable field="teacher" valueRangeProviderRefs="teacherRange"/> </entityDescriptor> <!-- 补充其他求解器配置:评分规则、终止条件等 --> <scoreDirectorFactory> <constraintProviderClass>替换为教学分配场景的约束类全类名</constraintProviderClass> </scoreDirectorFactory> <termination> <secondsSpentLimit>30</secondsSpentLimit> </termination> </solver-config>
第三步:编写课时排程场景的独立求解器配置
新建配置文件lectureSchedulingSolverConfig.xml,定义该场景下的映射规则:
<?xml version="1.0" encoding="UTF-8"?> <solver-config xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd"> <domainAccessType>REFLECTION</domainAccessType> <solutionDescriptor class="替换为你项目Schedule的全类名"> <!-- 定义时间段、教室可选值范围 --> <valueRangeProvider id="timeslotRange" field="timeslots"/> <valueRangeProvider id="roomRange" field="rooms"/> <!-- 定义问题事实属性 --> <problemFactCollectionProperty field="teachers"/> <problemFactCollectionProperty field="courseRounds"/> <problemFactCollectionProperty field="studentGroups"/> <!-- 定义规划实体集合 --> <planningEntityCollectionProperty field="lectures"/> <!-- 此处补充你定义的@PlanningScore对应字段的配置 --> </solutionDescriptor> <!-- 定义规划实体Lecture的映射规则 --> <entityDescriptor class="替换为你项目Lecture的全类名"> <planningVariable field="timeslot" valueRangeProviderRefs="timeslotRange"/> <planningVariable field="room" valueRangeProviderRefs="roomRange"/> </entityDescriptor> <!-- 补充其他求解器配置:评分规则、终止条件等 --> <scoreDirectorFactory> <constraintProviderClass>替换为课时排程场景的约束类全类名</constraintProviderClass> </scoreDirectorFactory> <termination> <secondsSpentLimit>60</secondsSpentLimit> </termination> </solver-config>
第四步:代码中分别加载配置创建求解器
// 初始化原始排课数据 Schedule initialSchedule = initScheduleData(); // 加载教学分配配置求解教师分配问题 SolverFactory<Schedule> teacherAssignmentFactory = SolverFactory.createFromXmlResource( "你的配置文件路径/teacherAssignmentSolverConfig.xml"); Solver<Schedule> teacherAssignmentSolver = teacherAssignmentFactory.buildSolver(); Schedule teacherAssignedSchedule = teacherAssignmentSolver.solve(initialSchedule); // 加载课时排程配置,基于教师分配结果求解排课问题 SolverFactory<Schedule> schedulingFactory = SolverFactory.createFromXmlResource( "你的配置文件路径/lectureSchedulingSolverConfig.xml"); Solver<Schedule> schedulingSolver = schedulingFactory.buildSolver(); Schedule finalSchedule = schedulingSolver.solve(teacherAssignedSchedule);
方案2:注解模式变通实现
如果你更习惯使用注解配置,也可以通过继承基础类的方式实现:
- 基础模型保持无注解,分别为两个场景创建子类
- 教学分配场景:创建
TeacherAssignmentSchedule继承Schedule加规划解注解,创建AssignableCourseRound继承CourseRound加规划实体注解 - 课时排程场景:创建
LectureSchedulingSchedule继承Schedule加规划解注解,创建SchedulableLecture继承Lecture加规划实体注解
- 教学分配场景:创建
- 运行时将基础数据转换为对应场景的子类实例求解即可,优势是注解配置更直观,缺点是需要额外定义一套子类。
内容的提问来源于stack exchange,提问作者aioobe
相关产品推荐
相关产品推荐

