I'm lately trying to implement a specific problem using an object-oriented approach. I get the main principles and its advantages, but I fail to apply it to a real world problem. Most examples one could find consist of Cats
or Dogs
being implementations of Animal
. These however don't give me enough understanding how to model below problem regarding another frequent example: a school administration system.
Imagine a school having Students
, Courses
, Professors
, and Notes
.
My implementation would be something like this:
class Person {
string name;
int age;
Person(string name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
double gpa;
Student(string name, int age) {
super(name, age);
}
}
class Professor extends Person {
string roomNumber;
Professor(string name, int age, string roomNumber) {
super(name, age);
this.roomNumber = roomNumber;
}
}
class Course {
string name;
Professor professor;
Students[] student;
Course(string name, Professor professor) {
this.name = name;
this.professor = professor;
this.students = new Student[];
}
void enrolStudent(Student student) {
students.add(student);
}
}
class Note {
Course course;
Student student;
double value;
Note(Course course, Student student, double value) {
this.course = course;
this.student = student;
this.value = value;
}
}
Now the Student
has a bunch of Notes
and we want to calculate its GPA. This could be either straightforward averaging its Notes
' values or more complex logic using weights and/or ignoring optional courses.
Now my question is: where do we put this logic? Ideally I would have a function double calculateGpa()
on Student
so you could call student.calculateGpa()
, but having this logic on Student
would break the SRP in my view. It also does not belong to any other class listed here. A class called GpaCalculator
or NotesManager
would be another guess but that seems to me too much like moving all the logic away from the domain and into classes that do not represent a real object but just actions (see also this answer).
If that would be the way to go here, why wouldn't I then just write a pure, static, stateless function in a class called NotesHelper
? Creating a manager class to just have one function double calculate()
, and using its instance instead of a static function feels to me like making it look like OOP while it isn't really. I feel like there should be a better approach, probably one I didn't think of, or maybe I am wrong here. Could you guys give me some pointers?
Student
model are 2 different reasons for rewriting theStudent
class. Should I update my question to include for example weights on Courses or on Notes? That would maybe make more clear why I'm hesitating adding calculation logic toStudent
.Student
orPerson
, nor is there any requirement for "OO" design to involve mixing data with behaviour in the same class. Perhaps yourStudent
andPerson
classes should be pure data entities with no behaviour, used for persistence and data modelling instead?new GPA(...)
... Or you know, whatever works for you and looks intuitive, testable, and extensible to you and your team...