Associations with numbers on both ends, one-to-one, one-to-many and many-to-many, written as fields kept in sync and checks that refuse what the diagram forbids.
A school has students, courses, teachers and lockers. None of them owns another: they only know each other, and the diagram's job here is the numbers at the line ends. Read each one as "how many of this end for one of the other end":
%% title: The school
classDiagram
class Teacher {
+name str
+courses list
}
class Course {
+code str
+students list
+enrol(student)
+drop(student)
}
class Student {
+name str
+courses list
+locker Locker
+assign_locker(locker)
+return_locker()
}
class Locker {
+number int
+owner Student
}
Teacher "1" -- "*" Course : teaches
Student "0..5" -- "*" Course : enrolled in
Student "0..1" -- "0..1" Locker : usesSo: a course has exactly one teacher and a teacher teaches any number of courses (one-to-many); a student takes at most 5 courses and a course has any number of students (many-to-many, with a cap); a student has at most one locker and a locker has at most one student (one-to-one, both optional).
Build this API (names are Python's; JavaScript and TypeScript use camelCase where they differ: assignLocker, returnLocker):
Teacher(name) with .name and .courses (starts empty).Course(code, teacher) with .code, .teacher (the same Teacher it was given) and .students (starts empty). The 1 is a check: a missing teacher (None / null) raises ValueError (JavaScript: Error whose message contains teacher). Building a course also adds it to teacher.courses, so both ends agree.Student(name) with .name, .courses (starts empty) and .locker (starts None / null).Locker(number) with .number and .owner (starts None / null).Course.enrol(student) adds the student to course.students and the course to student.courses: the two lists must always agree. A student already in the course raises ValueError (Error containing already). A student already in 5 courses raises ValueError (Error containing 5 courses), and nothing changes.Course.drop(student) removes both ends. A student who is not in the course raises ValueError (Error containing not enrolled).Student.assign_locker(locker) sets student.locker and locker.owner. A student who already has a locker raises ValueError (Error containing already has a locker); a locker that has an owner raises ValueError (Error containing taken). Neither refusal may change anything.Student.return_locker() clears both ends. A student with no locker raises ValueError (Error containing no locker).Write your code, then run 9 example tests and 3 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.