这是一个问题陈述: 我们有接口/超级班学生和老师
学生有两个实施/子课程,ScienceStudent和PhysicalEducationStudent
老师有ScienceTeacher和PhysicalEducationTeacher。
我们希望实现一个方法getMeetingPoint(Student s,Teacher t),它根据学生和教师的类型返回他们见面的地方。
例如, 如果它是 ScienceStudent 和 ScienceTeacher ,他们会在实验室见面 如果 PEStudent 和 PETeacher ,他们会在 Ground 上见面 如果它是 ScienceStudent 和 PETeacher ,反之亦然,他们会在自助餐厅会面
我们可以编写一个天真的方法,使用instanceof
进行检查。但问题是,当教师或学生扩展并且难以维护时,这变得复杂。
像这样的东西:
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";
}
.
.
.
}
}
另一种选择是写一个工厂,它接受学生和教师并返回类似MeetingPointDecision [Ground或Lab]的内容,但问题仍然存在。
我们可以使用任何好的模式,在添加新类时我们不必修改现有的类(或最小的修改),Say instanceof
ScienceStudent我们有ChemistryStudent,PhysicsStudent和ChemistryLab,PhysicsLab。
还有可能添加更多动作,这些动作在实现方面有所不同
学生和教师类型(访客是一个选项,但不确定如何实施两个决定课程)
有人可以建议一个好方法来实现这个吗?
谢谢!
答案 0 :(得分:2)
我会用地图来解决这个问题。关键应该识别教师+学生组合,价值将是会面点。对于键,我会组合类名。这是解决方案:
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();
}
}
逻辑部分位于静态ctor中,其中填充了地图。很容易支持未来的课程。
答案 1 :(得分:2)
这是一个有趣的话题,因为最近Eric Lippert撰写的文章讨论了这个问题。它分为五个部分:
代码是用C#语言编写的,但我认为至少从Java的角度来看它应该是可以理解的。
简而言之,您无法通过工厂或访客模式获得更好的结果。您的MeetingPointDecider
实施已经开始实施。如果你仍然需要一些可以不那么硬编码或映射的东西,可以试试sharonbn的解决方案或类似方法。
或者,如果您需要可扩展规则,您可以尝试类似Decorator模式的东西:
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) {
}
}
最后但不推荐,但如果您仍需要灵活性,可以尝试运行时编译该类并将其用作规则引擎。但不推荐。
注意:我没有回答原来的问题,因此社区维基回答。如果此答案格式错误,我将删除它。
答案 2 :(得分:0)
如果您将getMeetingKeyPart()方法添加到接口(学生和教师)并实施以返回每个学生和教师实施的特定关键部分,该怎么办。
E.g。 ScienceStudent返回&#34; ScienceStudent&#34;和ScienceTeacher回归&#34; ScienceTeacher&#34;。
然后,您可以定义.properties文件,其中为任何所需的组合键定义会合点。 E.g。
ScienceStudent-ScienceTeacher=Lab
PhysicalEducationStudent-PhysicalEducationTeacher=Ground
...
如果组合键不匹配,则返回&#34;自助餐厅&#34;
答案 3 :(得分:0)
假设您无法更改界面,您可以创建Faculty
枚举并添加对它的支持,以从类类型派生教师。
public enum Faculty {
SCIENCE("Lab", Arrays.asList(ScienceStudent.class, ScienceTeacher.class)),
PHYSICAL_EDUCATION("Ground", Arrays.asList(PhysicalEducationStudent.class, PhysicalEducationTeacher.class)),
UNKNOWN("Unknown", Collections.<Class<?>>emptyList());
private final List<Class<?>> types = new LinkedList<>();
public final String meetingPlace;
Faculty(String meetingPlace,
List<Class<?>> types) {
this.meetingPlace = meetingPlace;
this.types.addAll(types);
}
public static Faculty getFaculty(Class<?> type) {
Faculty faculty = UNKNOWN;
final Faculty[] values = values();
for (int i = 0; faculty == UNKNOWN && i < values.length; i++) {
for (Iterator<Class<?>> iterator = values[i].types.iterator(); faculty == UNKNOWN && iterator.hasNext(); ) {
final Class<?> acceptableType = iterator.next();
faculty = type.isAssignableFrom(acceptableType) ? values[i]
: UNKNOWN;
}
}
return faculty;
}
}
在你的会场决策中,你可以得到这些院系并进行比较。
final Faculty studentFaculty = Faculty.getFaculty(student.getClass());
final Faculty teacherFaculty = Faculty.getFaculty(teacher.getClass());
return studentFaculty == teacherFaculty ? teacherFaculty.meetingPlace
: "cafeteria";
理想情况下,您可以更改Teacher
和Student
接口以直接获取'Faculty',然后您就可以了。
final Faculty studentFaculty = student.getFaculty();
final Faculty teacherFaculty = teacher.getFaculty();
return studentFaculty == teacherFaculty ? teacherFaculty.meetingPlace
: "cafeteria";
当然,这并不总是可行的,因此是第一个解决方案。
答案 4 :(得分:0)
2个参数的访问者模式的工作方式与单个参数的工作方式相同。您只需要为方法参数使用具体实现,以便编译器可以根据调用上下文选择正确的方法。
public class MeetingPointDecider implements StudentTeacherVisitor {
Decision getMeetingPoint(ScienceStudent s, ScienceTeacher t) {
// return result
}
Decision getMeetingPoint(PEStudent s, PETeacher t) {
// return result
}
// etc.
}
当然,这可能不是您想要的,因为在调用特定的访问者方法时,您需要了解学生和教师的具体类型,以便在编译时解决问题。正如其他人建议您可以使用地图/属性方法。
答案 5 :(得分:0)
我将创建一个接口来对任何可以见面的人的行为进行建模。该接口将由学生,教师,体操运动员,科学家等实现。实现者将利用该接口中的默认行为,或使用其自己的行为将其覆盖。可以随时添加新的实现器。
public static void main(String... args) {
Meeter scienceTeacher = new ScienceTeacher();
Meeter scienceStudent = new ScienceStudent();
Meeter gymTeacher = new GymTeacher();
Meeter gymStudent = new GymStudent();
System.out.println("Science Teacher & Science Student meet in the " + scienceTeacher.findMeetingPointWith(scienceStudent));
System.out.println("Science Teacher & Gym Student meet in the " + scienceTeacher.findMeetingPointWith(gymStudent));
System.out.println("Gym Teacher & Science Student meet in the " + gymTeacher.findMeetingPointWith(scienceStudent));
System.out.println("Gym Teacher & Gym Student meet in the " + gymTeacher.findMeetingPointWith(gymStudent));
}
interface Meeter {
enum MeetingPoint { LAB, GYM, CAFETERIA }
MeetingPoint preferredMeetingPoint();
default MeetingPoint findMeetingPointWith(Meeter other) {
MeetingPoint myPreference = preferredMeetingPoint();
return myPreference == other.preferredMeetingPoint() ? myPreference : defaultMeetingPoint();
}
default MeetingPoint defaultMeetingPoint() {
return MeetingPoint.CAFETERIA;
}
}
interface Scientist extends Meeter {
@Override default MeetingPoint preferredMeetingPoint() {
return MeetingPoint.LAB;
}
}
interface Gymnast extends Meeter {
@Override default MeetingPoint preferredMeetingPoint() {
return MeetingPoint.GYM;
}
}
static class ScienceTeacher implements Scientist {}
static class ScienceStudent implements Scientist {}
static class GymTeacher implements Gymnast {}
static class GymStudent implements Gymnast {}
请注意,上述示例不是可交换的,即A符合B可能会产生与B符合A不同的结果。如果这是不希望的,请考虑向priority()
添加Meeter
方法,该方法可以确定比较顺序。