2015-05-26 8 views
7

Вот постановка задачи: У нас есть интерфейсы/супер-классы для студентов и преподавателейПосетитель шаблон для двух аргументов

Студент имеет две реализации/суб clasees, 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 [смолоть или Lab], но проблема остается. Есть ли хороший образец, который мы можем использовать, где нам не нужно изменять существующие классы (или минимальную модификацию) при добавлении нового класса. Скажем instanceof ScienceStudent у нас есть ChemistryStudent, PhysicsStudent и ChemistryLab, PhysicsLab. Существует также возможность добавления большего количества действий, который отличается в реализации на основе студентов и типа учителя (где посетитель вариант, но не уверен, как реализовать с двумя решающими классами)

Может кто-то пожалуйста, Подсказать хороший способ реализовать это?

Спасибо!

+0

Почему ученик должен быть примером PhysicalEducationTeacher ...'s instanceof PhysicalEducationTeacher' –

+0

Спасибо за уведомление. Это была опечатка. Исправлено. – pks

+1

Не было бы проще пойти обратным образом, и использовать фабрику * Лаборатория * будет возвращена кем-либо. Реализация интерфейса 'Науки' и * Земля * будет возвращена интерфейсом' Sports'. –

ответ

0

Что делать, если вы добавляете метод getMeetingKeyPart() к интерфейсам (Student and Teacher) и реализуете для возврата определенных ключевых частей для каждой реализации Student и Teacher.

E.g. ScienceStudent возвращает «ScienceStudent», а ScienceTeacher возвращает «ScienceTeacher».

Затем вы можете определить файл .properties, где точки встречи определены для любой желаемой комбинации клавиш. Например.

ScienceStudent-ScienceTeacher=Lab 
PhysicalEducationStudent-PhysicalEducationTeacher=Ground 
... 

Если нет совпадения для комбинации клавиш вы вернетесь «кафетерий»

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, где карта заполняется. Легко поддерживать будущие классы.

+0

Я думаю, что Cafeteria является значением по умолчанию. Таким образом, мы можем зарегистрировать только две первые пары и вернуть Cafeteria, если на карте нет значения. Или бросить UnsupportedOperationException («Пара не реализована») – Volatile

+0

спасибо, @Volatile, я отредактировал свой ответ, чтобы отразить ваш комментарий –

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"; 

Конечно, это не всегда возможно, следовательно, первое решение.

0

Шаблон посетителя для 2 аргументов будет работать так же, как и для одного аргумента. Вам просто нужно использовать конкретные реализации для параметров метода, чтобы компилятор мог выбрать правильный метод, основанный на контексте вызова.

public class MeetingPointDecider implements StudentTeacherVisitor { 

    Decision getMeetingPoint(ScienceStudent s, ScienceTeacher t) { 
     // return result 
    } 

    Decision getMeetingPoint(PEStudent s, PETeacher t) { 
     // return result 
    } 

    // etc. 
} 

Конечно, это не может быть то, что вы хотите, так как при вызове конкретного метода посетителя, вам нужно знать конкретные типы студентов и преподавателей, так что разрешение происходит во время компиляции. Как и другие, вы можете использовать подход Map/Properties.

1

Это интересная тема, потому что недавно Eric Lippert написал статью, в которой обсуждается это. Он разделен на пять частей:

код написан на C# язык, но я думаю, что это должно быть понятно, по крайней мере, с точки зрения Java.

Короче говоря, вы не получите лучшего результата с фабрикой или шаблоном посетителя. Ваша реализация MeetingPointDecider уже находится на пути. Если вам все еще нужно что-то, что может быть менее жестко запрограммировано или сопоставлено, попробуйте решение sharonbn или подобное.

Или, если вам нужно выдвижные правила, вы можете попробовать что-то подобное, как декоратор:

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) { 

    } 
} 

Последнее, но не рекомендуется, но если вы все еще нуждаетесь в гибкости, вы можете попробовать во время выполнения компиляции класса и использования это как правило. Но не рекомендуется.

Примечание: Я не отвечаю на исходный вопрос, поэтому ответ вики сообщества. Если этот формат ответа неправильный, я удалю его.

 Смежные вопросы

  • Нет связанных вопросов^_^