I'm developing a hobby project to properly understand encapsulation, what classes can be responsible for, and rules. I asked for a code review and assistance in another forum, but I don't agree with the approach given.
I have the following requirements:
- An international student requires documents to complete the registration process, but domestic students don't.
StudentStatus Interface:
public interface StudentStatus {
Collection<String> retrieveDocuments();
StudentType retrieveStatus();
}
public final class Domestic implements StudentStatus {
private final StudentType type;
private final Collection<String> documents;
public Domestic() {
this.type = StudentType.Domestic;
this.documents = Collections.emptyList();
}
@Override
public Collection<String> retrieveDocuments() {
return this.documents;
}
@Override
public StudentType retrieveStatus() {
return type;
}
}
public final class International implements StudentStatus {
private final StudentType type;
private Collection<String> documents;
public International(Collection<String> documents) {
this.type = StudentType.International;
this.documents = Collections.unmodifiableCollection(documents);
}
@Override
public Collection<String> retrieveDocuments() {
return Collections.unmodifiableCollection(documents);
}
@Override
public StudentType retrieveStatus() {
return type;
}
}
Student class:
public final class Student {
//left out constructor and getters for other attributes.
public Collection<String> retrieveDocuments() {
return status.retrieveDocuments();
}
public StudentType retrieveStatus() {
return status.retrieveStatus();
}
public boolean isVerified(StudentType type) {
return this.retrieveStatus() == type;
}
}
University class:
public class University {
private final Map<Student,Collection<String>> registeredStudents;
private final StudentType type;
public University()
{
registeredStudents = new HashMap<Student,Collection<String>>();
type = StudentType.International;
}
public void add(Student student){
if (student.isVerified(type)){
registeredStudents.put(student, student.retrieveDocuments());
}else {
//throw an exception or handle error accordingly
}
}
}
Before I continue, I understand that this is a really over simplified application process. In the real world, a lot more has to happen before a Student can register. The student may have to go through entrance exams, and payment before registration begins. Also, in a realistic environment, this information would probably be stored in a database that the campus employees can access.
In the other forum, the conversation went into what information is being given out, and approaches were given.
- Have a rule class, that takes the Student object and verifies that it is in fact international and has documents.
The problem I have with this, is you're still going to have to ask the Student his/her status either with the retriveStatus()
or isVerified()
, I don't really see how to do it any other way.
- Pass the Student and collection of documents separately to be added to the Map.
In the real world, the University set the rule as stated above and it's responsibility is to check if International students have documentation.
When I suggested the approach above with the add(Student student)
they stated it wasn't a good idea because the rules can change, and you'll have to change the Student class as well as the University class.
However, in the real world, a student is well aware of his/her status and if he/she is domestic/international and in possession of documents that can be given to the school.
Given the above approach, is writing the add method this way a good idea? Is there a better approach than the add method?
tl;dr - If a Student has to follow the rules set by the University, how then would the Student object communicate with the University to get the data so that the University can ensure the student object is complying with the rules without breaking encapsulation?