Как найти тип без использования instanceof? - PullRequest
3 голосов
/ 17 сентября 2011

У меня List интерфейсного типа Criteria в моем классе Query.

List<Criteria> criteria = new ArrayList<Criteria>();

У меня есть несколько конкретных реализаций Criteria. Я хочу дать Query метод, который перебирает мой список criteria и, в зависимости от конкретного типа, выполняет некоторую логику.

В настоящее время я делаю это с instanceof примерно так:

for(Criteria c : criteria) {
    if(c instanceof ContextualCriteria){
        // logic
    }
    ...
}

Это единственный / лучший способ?

Ответы [ 7 ]

8 голосов
/ 17 сентября 2011

Разумно ли логика принадлежит самой Criteria? Если это так, поместите его в интерфейс Criteria и реализуйте его соответствующим образом для каждого конкретного класса, реализующего интерфейс Criteria. Это, очевидно, хороший полиморфный подход.

К сожалению, в реальной жизни ОО не всегда работает так просто - иногда не имеет смысла помещать поведение для каждого типа в сам тип, поэтому вам, возможно, придется использовать instanceof вместо этого. Вы могли бы потенциально иметь карту из "класса критериев" к какому-либо интерфейсу, представляющему действие, которое нужно выполнить, но это может в конечном итоге стать еще более запутанным.

Двойная отправка через шаблон посетителя может иногда немного улучшить вещи - так что логика все еще может быть в отдельных методах в вашем "вызывающем" классе, но каждый Criteria можно вызвать обратно к нужному методу через единственный интерфейсный метод. Лично я склонен считать, что это усиливает сцепление и быстро становится ужасным, но другие клянутся этим.

3 голосов
/ 17 сентября 2011

Если логика не принадлежит критериям, как предлагает Джон Скит, вы можете использовать шаблон посетителя .

В ConcreteCriteria:

public void accept(CriteriaVisitor v) {
    v.visit(this);
}

Вкод клиента:

public void method() {
    for (Criteria c : criteria) {
        c.accept(this);
    }
}

public void visit(ConcreteCriteria c) {
    // do logic here
}

public void visit(Criteria c) {
    // othervise...
}

Это избавляет от instanceof, но будьте осторожны, я обнаружил, что этот шаблон трудно понять, если вы не знакомы с кодом.

1 голос
/ 17 сентября 2011

Вы также можете реализовать логику в классах, которые реализуют критерии

public interface Criteria {
  public void logic();
}

Имеют несколько реализаций в таких классах, как ContextualCriteria. И ваш цикл будет выглядеть чистым:

for(Criteria c : criteria) {  
   c.logic();
}  
1 голос
/ 17 сентября 2011

Интерфейс на полпути к шаблону стратегии! Чтобы варьировать логику в зависимости от типа, по возможности, поместите ее за интерфейс, чтобы в Criteria была функция doLogic (). Вы можете передавать этому методу любые параметры, которые вам могут понадобиться изменить в вызывающем коде, или возвращать новую информацию - это очень специфично для имплементации и трудно советовать из рассматриваемого кода.

Если все идет хорошо, ваш код вызова заканчивается

for (Criteria c : criteria) {
    c.doLogic();
}
1 голос
/ 17 сентября 2011

Ну, вы можете ...

if (ContextualCriteria.class.equals(c.getClass()) {

... хотя это просто причудливый способ написания instanceof. (Ну, почти: это проверяет, является ли это именно класс, а не класс подкласса - для этого вы хотите isAssignableFrom()).

Правильный способ избавиться от запаха - реализовать полиморфный метод в Criteria, который переопределяется, например, в подклассах.

0 голосов
/ 17 сентября 2011

В этом нет ничего плохого.

Люди, которые никогда не пользовались instanceof, писали только игрушечные приложения.

0 голосов
/ 17 сентября 2011

Вы также можете использовать:

for(Criteria c : criteria) {
   if(c.getClass() == ContextualCriteria.class){
       // logic
   }
   if ...
}

Обратите внимание, что Object # getClass () возвращает тип времени выполнения c, поэтому вы не можете надежно использовать его, если ContextualCriteria может быть разделено на подклассы, например.Для этого вам нужно использовать Class # isAssignableFrom () :

for(Criteria c : criteria) {
   if(ContextualCriteria.class.isAssignableFrom(c)){
       // logic
   }
   if ...
}
...