I have two classes Student and Tutor. Tutor is basically a student (Tutor extends Student) who has facultyID. Once his contract is complete, he returns to being just a student. So can I somehow convert him back to his "previous" roll of student?
What you really want to do here is use composition and not inheritance. Keep all your objects as type Student
, and then temporarily assign the behaviour of a TutorRole
as it is required to each instance of Student
.
With this design your Student
class will contain a property (member variable) of type TutorRole
that you can add or remove at runtime. Adding an isTutor()
method will allow you to detemine whether a Student is a Tutor at runtime in a clear and concise manner.
The TutorRole
class will encapsulate the behaviour (i.e. methods) of being a Tutor.
/*
* The TutorRole can be set at runtime
*/
public class Student {
private String facultyId;
private TutorRole tutorRole = null;
public boolean isTutor() {
return !(tutorRole == null);
}
public void doTutorStuff() {
if(isTutor()) {
tutorRole.doTutorStuff();
}
else {
throw new NotTutorException();
}
}
public void setTutorRole(TutorRole tutorRole) {
this.tutorRole = tutorRole;
}
}
/*
* Ideally this class should implement a generic interface, but I'll keep this simple
*/
public class TutorRole {
public void doTutorStuff() {
// implementation here
}
}
/*
* Now let's use our classes...
*/
Student st = new Student(); // not a tutor
st.setTutorRole(new TutorRole()); // now a tutor
if(st.isTutor()) {
st.doTutorStuff();
}
st.setTutorRole(null); // not a tutor anymore
An alternative approach is to have a Tutor
class contain a reference to a Student
object, but it depends on how you are going to be interacting with the Student and Tutor objects on which way around you want to code this.
I think, this screams containment and interface programming.
How about this:
interface IStudent
{
String getName();
int getStudentId();
}
interface IFacultyMember
{
int getFacultyId( );
}
class Student
implements IStudent
{
String name;
int id;
public String getName( ) { return name; }
public int getStudentId( ) { return id; }
}
class Tutor
implements IStudent, IFacultyMember
{
Student student;
int facultyId;
public Tutor ( Student student, int facultyId )
{
this.student = student;
this.facultyId = facultyId;
}
public String getName( ) { return student.getName( ); }
public int getStudentId( ) { return student.getStudentId( ); }
public int getFacultyId( ) { return facultyId; };
}
This way, your Student remains a student, even if it moves to the Tutor position. When Tutor's term expires you just GC the tutor record.
Student's record, on the other hand will still be available in Central Services.
Once you create an instance of some type (for example, Tutor
), that's the runtime type that instance is gonna have. This can't be changed anymore.
Some alternatives:
Student
some constructor that accepts another Student
instance and copies over the relevant fields. A so-called copy constructor. That way, you could easily create a new Student
instance based on your Tutor
and then use that instead.FacultyMember
class or something and turn Student
and Tutor
into roles. Then you can change the role of a FacultyMember
later on.You can cast the Tutor as a Student so your code treats him as such at compile time, but the object will remain a Tutor, so calling any overridden methods will cause the Tutor class's version to be called.
The only way to do the kind of "conversion" you're looking for is to create a new Student object and give it all the properties that the Tutor has.
Since you're finding that you need to do this conversion, you may want to re-think the class structure. For example, maybe both Students and Tutors should really just be Persons, and each Person can have the Role of Student or Teacher.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With