10.2. 对域对象建模
红帽构建的 OptaPlanner timetable 项目旨在为每个时间插槽和房间分配。要做到这一点,添加三个类、Timeslot、Lesson 和 Room,如下图所示:

Timeslot
Timeslot 类代表在教授课程时的时间间隔,例如: 星期一 10:30 - 11:30 或 Tuesday 13:30 - 14:30。在这个示例中,所有时间插槽都具有相同的持续时间,在午餐或其他中断期间都没有时间插槽。
时间插槽没有日期,因为高校的计划仅每周重复。不需要 持续规划。一个时间被认为是个问题,因为在解决 过程中,实例不会发生改变。此类类不需要任何特定于 OptaPlanner 的注解。
room
Room 类代表教授课程的位置,例如 Room A 或 Room B。在这个示例中,所有房间都没有容量限制,它们可以适应所有课程。
空间 实例在解决过程中不会改变,因此 Room 也是 问题事实。
courseon
在课程学习期间,教员将教授一门主题给一组学员,例如: A.Turing for 9th grade 或 Chemistry,M.Curie 为 10th grade。如果每周向同一学员组教授了多次主题,则有多个较少实例,只有 id 区分。例如,每周的第 9 个学分有 6 个学分。
在解决期间,OptaPlanner 会改变下课的计时和 房间 字段,以将每个上课时间分配到一个时间插槽和房间。 因为 OptaPlanner 更改了这些字段,所以 Lesson 是一个 计划实体 :

上图中的大多数字段都包含输入数据,但 orange 字段除外。在输入数据中未分配(空),并在输出数据中分配(非 )字段的不计时和 null房间 字段。在解决过程中,OptaPlanner 会更改这些字段。此类字段称为规划变量。为了使 OptaPlanner 能够识别它们,lot 和 room 字段都需要 @PlanningVariable 注解。它们包含类 Lesson,需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:package com.example.domain; import java.time.DayOfWeek; import java.time.LocalTime; public class Timeslot { private DayOfWeek dayOfWeek; private LocalTime startTime; private LocalTime endTime; private Timeslot() { } public Timeslot(DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) { this.dayOfWeek = dayOfWeek; this.startTime = startTime; this.endTime = endTime; } @Override public String toString() { return dayOfWeek + " " + startTime.toString(); } // ******************************** // Getters and setters // ******************************** public DayOfWeek getDayOfWeek() { return dayOfWeek; } public LocalTime getStartTime() { return startTime; } public LocalTime getEndTime() { return endTime; } }注意
toString ()方法保持输出短,以便更轻松地读取 OptaPlanner 的DEBUG或TRACE日志,如后文所示。创建
src/main/java/com/example/domain/Room.java类:package com.example.domain; public class Room { private String name; private Room() { } public Room(String name) { this.name = name; } @Override public String toString() { return name; } // ******************************** // Getters and setters // ******************************** public String getName() { return name; } }创建
src/main/java/com/example/domain/Lesson.java类:package com.example.domain; import org.optaplanner.core.api.domain.entity.PlanningEntity; import org.optaplanner.core.api.domain.variable.PlanningVariable; @PlanningEntity public class Lesson { private Long id; private String subject; private String teacher; private String studentGroup; @PlanningVariable(valueRangeProviderRefs = "timeslotRange") private Timeslot timeslot; @PlanningVariable(valueRangeProviderRefs = "roomRange") private Room room; private Lesson() { } public Lesson(Long id, String subject, String teacher, String studentGroup) { this.id = id; this.subject = subject; this.teacher = teacher; this.studentGroup = studentGroup; } @Override public String toString() { return subject + "(" + id + ")"; } // ******************************** // Getters and setters // ******************************** public Long getId() { return id; } public String getSubject() { return subject; } public String getTeacher() { return teacher; } public String getStudentGroup() { return studentGroup; } public Timeslot getTimeslot() { return timeslot; } public void setTimeslot(Timeslot timeslot) { this.timeslot = timeslot; } public Room getRoom() { return room; } public void setRoom(Room room) { this.room = room; } }Lesson类具有@PlanningEntity注释,因此 OptaPlanner 知道该类在解决问题过程中发生了变化,因为它包含一个或多个规划变量。timeslot字段具有@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。要查找分配给此字段的潜在Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到提供List<Timeslot> 来选择的值范围供应商。有关值范围供应商的信息,请参阅 第 10.4 节 “在规划解决方案中收集域对象”。room字段也具有@PlanningVariable注释,理由相同。