12.2. 对域对象建模
红帽构建的 OptaPlanner 时间表项目的目标是将每节分配给一个时间段和一个房间。为此,请添加三个类: Timeslot
、Lesson
和 Room
,如下图所示:
Timeslot
Timeslot
类表示在未教授课程的时间间隔,例如: Monday 10:30 - 11:30
或 Tuesday 13:30 - 14:30
- 14:30。在这个示例中,所有时间插槽都有相同的持续时间,且在 lunch 或其他中断期间没有时间插槽。
时间段没有日期,因为高校的计划只每周重复。无法进行持续 规划。次性被称为 问题事实,因为在解决过程中不会有定时实例变化。此类类不需要任何特定于 OptaPlanner 的注解。
room
Room
类代表了课程讲授的方式,例如 Room A
或 Room B
。在本例中,所有空间都没有容量限制,它们可容纳所有课时。
房间
实例在解决过程中不会改变,因此 Room
也是 问题。
lesson
在课时,由课课课表示,向班级教授一个主题,例如: A.Turing for a 9th grade
或 Chemistry by M.Curie 为 10th grade
。如果一个主题每周都会为同一学员组讲授多次,则有多个小的、只能由
id
区分的实例。例如,第 9 分之时,每周有 6 个数学。
在寻求过程中,OptaPlanner 改变了 lesson 类的 timelot 和 room
字段,从而将每个较小的时间分配给一个时间段和一个房间。
因为 OptaPlanner 更改了这些字段,
所以
lesson 是一个 规划实体 :
上图中的大多数字段包含输入数据,但 orange 字段除外。在输入数据中 未分配的
时间和房间
字段(null
),并在输出数据中分配(而不是 null
)。OptaPlanner 在解决过程中更改这些字段。这些字段称为规划变量。为了 OptaPlanner 可以识别它们,timelot 和 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
或 abrt 日志,如稍后所示。创建
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 知道这个类在解决过程中发生了变化,因为它包含一个或多个计划变量。time
lot
字段具有@PlanningVariable
注释,因此 OptaPlanner 知道它可以更改其值。为了查找要分配给此字段的潜在的Timeslot
实例,OptaPlanner 使用valueRangeProviderRefs
属性连接到一个提供List<Timeslot>
的值供应商。有关值范围供应商的信息,请参阅 第 12.4 节 “在规划解决方案中收集域对象”。出于同样原因,
room
字段还具有@PlanningVariable
注释。