Visitor Pattern for two arguments
Here is a problem statement: 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, if its a ScienceStudent and ScienceTeacher they meet at Lab if PEStudent and PETeacher they meet on the Ground and if its a ScienceStudent and PETeacher or vice versa, they meet at cafeteria
We can write a naive method, which checks using instanceof
. But the problem is, 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)
Can someone please suggest a good way to implement this?
Thanks!
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.
This is interesting topic because recently Eric Lippert has written article that discuss about this. It is divided in five parts:
The code is written in C# language but I think it should be understandable enough from Java perspective, at least.
In short, you won't get better result with factory or visitor pattern. Your MeetingPointDecider
implementation is already on track. If you still need something that can be less hardcoded or mapped, try sharonbn's solution or similar.
Or if you need extendable rules, you can try something similar like Decorator pattern:
public class MeetingPointDecider {
// the rules, you can add/construct it the way you want
Iterable<MeetingPointDeciderRule> rules;
string defaultValue;
getMeetingPoint(Student s,Teacher t) {
string result;
for(MeetingPointDeciderRule rule : rules){
result = rule.getMeetingPoint(s, t);
//check whether the result is valid and not null
//if valid, return result
}
//if not valid, return default value
return defaultValue;
}
}
//this can be easily extended
public abstract class MeetingPointDeciderRule {
getMeetingPoint(Student s,Teacher t) {
}
}
Last but not recommended, but if you still need the flexibility, you can try to runtime compile the class and use it as rule engine. But not recommended.
Note: I am not answering the original question hence the community wiki answer. If this answer format is wrong, I will delete it.
What if you add a getMeetingKeyPart() method to the interfaces (Student and Teacher) and implement to return specific key parts for each Student and Teacher implementation.
Eg ScienceStudent returns "ScienceStudent" and ScienceTeacher returns "ScienceTeacher".
Then you can define a .properties file where meeting points are defined for any desired key combination. Eg
ScienceStudent-ScienceTeacher=Lab
PhysicalEducationStudent-PhysicalEducationTeacher=Ground
...
If there is no match for the key combination you return "cafeteria"
链接地址: http://www.djcxy.com/p/26474.html上一篇: 如何在Dockerfile RUN中使用管道(ioredirection)?
下一篇: 访问者模式的两个参数