We have interfaces/super classes: Student and Teacher.
Student has two implementations/sub clasees, ScienceStudent and PhysicalEducationStudent
Teacher has ScienceTeacher and PhysicalEducationTeacher.
We want to implement a method getMeetingPoint(Student s, Teacher t) which returns a place where they meet based on the type of Student and Teacher.
For example,
We can write a naive method, which checks using instanceof
.
The problem is that this becomes complex when Teacher or Student gets extended, and tough to maintain. Something like this:
public class MeetingPointDecider {
getMeetingPoint(Student s,Teacher t) {
if(s instanceof ScienceStudent && t instanceof ScienceTeacher) {
return "Lab";
} else if (s instanceof PhysicalEducationStudent && t instanceof PhysicalEducationTeacher) {
return "GRound";
}
.
.
.
}
}
Another option is writing a factory, which accepts a Student and a Teacher and returns something like MeetingPointDecision [Ground or Lab], but the problem persists.
Is there any good pattern we can use, where we do not have to modify existing classes (or minimal modification) when a new class is added?
Say instanceof
ScienceStudent we have ChemistryStudent, PhysicsStudent and ChemistryLab, PhysicsLab.
There is also a chance of adding more actions, which differs in implementation based on the Student and Teacher type ( Where Visitor is an option, but not sure how to implement with two deciding classes).
Visitor design pattern is one of the behavioral design patterns. It is used when we have to perform an operation on a group of similar kind of Objects. With the help of visitor pattern, we can move the operational logic from the objects to another class.
The Visitor pattern represents an operation to be performed on the elements of an object structure without changing the classes on which it operates. This pattern can be observed in the operation of a taxi company. When a person calls a taxi company (accepting a visitor), the company dispatches a cab to the customer.
There are five major components of visitor pattern: Client: It is a class that acts as a consumer of the classes of the design pattern. It has the authority to access the data structure objects. It also instructs them to accept a Visitor that performs the appropriate processing.
In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures.
I would solve this using a map. The key should identify the teacher + student combination and the value would be the meeting point. for the key I would combine the class names. Here is the solution:
public class MeetingPointDecider
{
public enum MeetingPoint { Ground, Lab, Cafeteria }
private static MeetingPoint defaultmp = MeetingPoint.Cafeteria;
private static Map<String, MeetingPoint> studentTeacherCombinations = new HashMap<>();
static {
studentTeacherCombinations.put(getMapKey(ScienceTeacher.class, ScienceStudent.class), MeetingPoint.Lab);
studentTeacherCombinations.put(getMapKey(PETeacher.class , PEStudent.class) , MeetingPoint.Ground);
}
public static MeetingPoint getMeetingPoint(Student s,Teacher t)
{
String mapKey = getMapKey(t.getClass(), s.getClass());
return studentTeacherCombinations.containsKey(mapKey) ?
studentTeacherCombinations.get(mapKey) : defaultmp;
}
private static String getMapKey (Class<? extends Teacher> tCls, Class<? extends Student> sCls)
{
return tCls.getName() + "_" + sCls.getName();
}
}
The logic part is in the static ctor where the map gets populated. It is easy to support future classes.
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