18.2. 对域对象建模
红帽构建的 OptaPlanner timetable 项目的目标是为每个课程分配时间插槽和房间。要做到这一点,请添加三个类,Timeslot
,
lesson , 和 Room
,如下图所示:
timeslot
Timeslot
类代表了在课程时的间隔,例如 Monday 10:30 - 11:30
或 Tuesday 13:30 - 14:30
。在这个示例中,所有时间插槽都有相同的持续时间,在 lunch 或其他间没有时间插槽。
时间段没有日期,因为高校计划每周都会重复。不需要 持续规划。一个 timeslot 被称为 问题,因为在 解决过程中没有 Timeslot
实例改变。此类类不需要任何 OptaPlanner 特定注解。
房间
Room
类代表一个跟踪课程的位置,例如 Room A
或 Room B
。在这个示例中,所有房间都没有容量限制,它们可以容纳所有课程。
房间
实例在解决时 不会改变
,因此也 是一个问题。
lesson
在短时间内,由 Lesson
类表示,教员向一组学员提供主题,例如 Math by A.Turing for 9th grade
或 Chemistry by M.Curie for 10th grade
。如果每周由同一生组多次学习一个主题,则有多个仅通过 id
区分的 lesson 实例。例如,第 9 个评级每周有 6 个数个。
在解决期间,OptaPlanner 更改了 Lesson
类的 timeslot
和 room
字段,为每个课程分配时间插槽和房间。因为 OptaPlanner 更改这些字段,所以 lesson 是一个 规划实体 :
上图中的大部分字段包含输入数据,但 orange 字段除外。一个 lesson 的 timeslot
和 room
字段在输入数据中未分配(空 ),并在输出数据中分配(非
)。OptaPlanner 在解决过程中更改这些字段。此类字段称为计划变量。为了让 OptaPlanner 识别它们,null
timeslot
和 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>
; 的值范围供应商。有关值范围供应商的信息,请参阅 第 18.4 节 “在规划解决方案中收集域对象”。room
字段还具有@PlanningVariable
注释,原因相同。