Trying to create a simple Course Management application using SOLID principles and best practices for Object Oriented Programming. Looking for feedback on the library I created in src/main/lib of the github project below. Example usage is shown in src/main/app if you are curious, but I am less concerned with feedback on that part.
Some requirements of the application:
- Create new courses
- Create new students
- Create new rooms
- Add students to course
- Assign room to course
- View all courses for a student
- View all students in a course
- View all courses in a room
In addition to a general code review, I am also have some specific questions:
What should the relationship between students and courses look like? I currently have Student containing a set of Courses, and Course containing a set of Students. This was mainly due to the "View" requirements above, but I am not sure if this is a bad practice to have the relationship going both ways.
For adding a Student to a Course, I have a Registrar interface with an enroll method, which should do some simple validation (does student have course at same time, is course full, etc.). The enroll method in Registrar calls student.enroll(course) and course.enroll(student), but a side-effect of this is that I needed to make a public method in Student and Course called enroll. The problem here is that I want all enrollment to go through Registrar.enroll(...), but I am wondering if it is a concern that the other enroll methods are public when they should never actually be called by anyone other than the Registrar.
Some example code (full code can be found in github):
public class Course {
private String name;
private Calendar time;
private HashSet<Student> students;
private Room room;
public Course(String name, Calendar time) {
this.name = name;
this.time = time;
this.students = new HashSet<>();
}
public HashSet<Student> students() {
return students;
}
public void enroll(Student student) {
students.add(student);
}
@Override public String toString() {
return String.format("Course name: %s%nCourse Time: %s", this.name, this.time.getTime().toString());
}
public Calendar getTime() {
return time;
}
public void setRoom(Room room) {
this.room = room;
}
public Room getRoom() {
return room;
}
}
Student:
public class Student {
private String name;
private HashSet<Course> courses;
public Student(String name) {
this.name = name;
this.courses = new HashSet<>();
}
@Override public String toString() {
return name;
}
public HashSet<Course> courses() {
return this.courses;
}
public void enroll(Course course) {
courses.add(course);
}
}
Registrar Implementation:
public class RegistrarImpl implements Registrar {
private HashSet<Course> courses = new HashSet<>();
private HashSet<Student> students = new HashSet<>();
@Override
public void registerCourse(Course course) {
courses.add(course);
}
@Override
public void registerStudent(Student student) {
students.add(student);
}
@Override
public HashSet<Course> allCourses() {
return courses;
}
@Override
public HashSet<Student> allStudents() {
return students;
}
@Override
public void enroll(Course course, Student student) throws EnrollmentException {
if(!courses.contains(course)) {
throw new EnrollmentException("Course has not been registered.");
} else if(!students.contains(student)) {
throw new EnrollmentException("Student has not been registered.");
}
for(Course c : student.courses()) {
if(c.getTime().getTime().getTime() == course.getTime().getTime().getTime()) {
throw new EnrollmentException("Student already has class at this time.");
}
}
course.enroll(student);
student.enroll(course);
}
}