2010-10-15 1 views
0

Является ли следующая реализация java шаблона посетителя использованием дженериков, достаточно общего для того, чтобы быть полезным? (Я полагаю, это так).Общий шаблон посетителя в java

Может ли это быть каким-то образом улучшено? Очень важно, чтобы с помощью анонимных классов было легко работать с вызовами. Благодарю.

(Пример использования):

Vector<Number> numbers = new Vector<Number>(); 

     numbers.add(new Double(1.2)); 
     numbers.add(new Float(-1.2)); 
     numbers.add(new Double(4.8)); 
     numbers.add(new Float(-3.4)); 
     numbers.add(new Long(123456)); 
     numbers.add(new Short("14")); 

     For.each(numbers, new Visitor<Number>() { 
      public void doIt(Double n) { 
       System.out.println("doIt() for double: " + n); 
      } 
      public void doIt(Float n) { 
       System.out.println("doIt() for float: " + n); 
      } 
      public void doIt(Number n) { 
       System.out.println("doIt() for Number: " + n); 
      } 
     }); 

     Visitor<Number> visi = new Visitor<Number>() { 
      private StringBuffer all = new StringBuffer(); 
      public void doIt(Number n) { 
       System.out.println("doIt() for Number: " + n); 
       all.append(n.toString() + " "); 
      } 
      public Object getResult() { 
       return all; 
      } 
     }; 

     For.each(numbers, visi); 

     System.out.println ("all -> " + visi.getResult()); 

Определения:

//............................................ 
abstract class Visitor<T> { 
    public void visit(T n) { 
     try { 
      this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n); 
     } catch (Exception ex) { 
      doIt((T) n); 
     } 
    } 
    public void doIt(T n) { 
     System.out.println("doIt() for base " + n); 
    } 
    public Object getResult() { 
     return null; 
    } 
} // class 

//............................................ 
class For { 
    public static <T> void each (Collection<T> c, Visitor<T> f) { 
     for (T v : c) { 
      f.visit(v); 
     } 
    } //() 
} // class 
+3

as donroby указывает, что ваш код не полностью соответствует шаблону посетителя. Я думаю, что в нем есть больше шаблонов стратегии (другие мнения могут отличаться). Ваш код напоминает мне Collections.sort (List, Comparator), поэтому у него может быть полезная информация. – josefx

+1

Вчера я увидел * превосходный пример шаблона посетителя в ответе [aioobe] (http://stackoverflow.com/questions/3930808/is-there-a-better-option-for-this-code). Прочитайте весь вопрос, это сделает весь смысл шаблона более понятным. – BalusC

+0

@josefx Является ли новая версия шаблоном посетителя? (Смотри ниже). – cibercitizen1

ответ

2

Благодаря ответ donroby о моем исходном коде не реализующем шаблона посетителя я пришел к этой новой версии.

Я предполагаю, что теперь он реализует шаблон посетителя без необходимости изменения посещенного элемента с помощью метода accept(). В любом случае, он может вызвать правильный метод в зависимости от типа элемента (я думаю, это миссия для accept()), благодаря отражению.

Во-первых, пример использования:

Vector<Number> numbers = new Vector<Number>(); 

    numbers.add(new Double(1.2)); 
    numbers.add(new Float(-1.2)); 
    numbers.add(new Double(4.8)); 
    numbers.add(new Float(-3.4)); 
    numbers.add(new Long(123456)); 
    numbers.add(new Short("14")); 

    For.each(numbers, new Visitor<Number>() { 
     public void doIt(Double n) { 
      System.out.println("doIt() for double: " + n); 
     } 
     public void doIt(Float n) { 
      System.out.println("doIt() for float: " + n); 
     } 
     public void doIt(Number n) { 
      System.out.println("doIt() for Number: " + n); 
     } 
    }); 

Это производит этот выход

 
doIt() for double: 1.2 
doIt() for float: -1.2 
doIt() for double: 4.8 
doIt() for float: -3.4 
doIt() for Number: 123456 
doIt() for Number: 14 

И, наконец, код

abstract class Visitor<T> { 
public void visit(T n) { 
    try { 
     this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n); 
    } catch (Exception ex) { 
     doIt((T) n); 
    } 
} 
public void doIt(T n) { 
    System.out.println("doIt() for base " + n); 
} 
public Object getResult() { 
    return null; 
} 

}

class For { 
public static <T> void each (Collection<T> c, Visitor<T> f) { 
    for (T v : c) { 
     f.visit(v); 
    } 
} //() 

}

+2

Да, это реализует шаблон посетителя с отражением вместо двойной отправки. Одна проблема: строка doIt ((T) n) всегда приведет к вызову doIt (Object), поскольку общий тип будет удален со стороны компилятора. – josefx

+1

Поскольку вы задали вопрос о том, как улучшить код, вместо сохранения результата в вашем посетителе (ваш первый пример), вы можете использовать локальную переменную в каждом() для ее сохранения, передать ее посетителю в качестве параметра и вернуть ее на конец каждого(). Таким образом, у вас не будет состояния в посетителе. – josefx

+0

@josefx Возможно, я что-то пропустил, но doIt (Object) не называется (afaik). В моем векторе пример doIt (Double) и doIt (Float) вызываются для удвоений и поплавков. doIt (Number) вызывается для других типов. – cibercitizen1

6

Это не Visitor Pattern.

Посетитель характеризуется видом, имеющим метод accept(Visitor v), который взаимодействует с методом посещения посетителя, принимая в качестве параметра visitee и перегружая для изменяющегося типа визитера, образуя механизм «двойной отправки».

Цитирование из раздела «Применимость» для посетителей в Design Patterns:

Используйте паттерн Visitor, когда
  • структуры объекта содержит множество классов объектов с различными интерфейсами, и вы хотите выполнить операции на этих объектах, которые зависят от их конкретных классов.
  • много различных и не связанные между собой операций должны быть выполнены на объектов в структуре объекта, и вы хотите, чтобы избежать «загрязняющих» их классов с этими операциями. Посетитель позволяет вам связать связанные операции, определяя их в одном классе. Когда структура объекта разделяется многими приложениями , используйте Visitor для ввода операций только в таких приложениях , которые в них нуждаются.
  • классы, определяющие структуру объекта, редко меняются, но вы часто хотите определить новые операции над структурой. Изменение классов объектов требует переопределения интерфейса для всех посетителей, что потенциально является дорогостоящим. Если структура объектов меняется часто, то это , вероятно, лучше определить операции в этих классах.

Так этот шаблон для работы с подобным opertaions на объектах различных типов. В ваших примерах объекты, которые вы вызываете посетителей, могут иметь дело только с одним типом.

В вашем ответе, в котором рассматривается использование рефлексии для обработки нескольких типов (что, кстати, было бы лучше сделано как редактирование вопроса или как отдельный вопрос), вы избегаете создания метода accept(Visitor v) в посещенных классах используя отражение, которое в какой-то степени достигает одной и той же цели, но несколько неудобно. Я бы все равно сопротивлялся называть его внедрением Visitor.

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

Это больше похоже на Strategy Pattern или Function Object, и если вы переименуете общий класс таким образом, который отражает это, это действительно полезно, и ваше использование аналогично обычным шаблонам обработки списка на функциональных языках.

То, что я, скорее всего, делать с кодом от вопроса переименовывать Visitor<T> в Operation<T> и переименовать visit(T t) в execute(T t) или apply(T t), мышление из Operation как Function без возвращаемого значения. Я фактически использовал именно это способами, подобными тому, что вы делаете, и использовал аналогичную тактику для сбора «сопоставления» с использованием общих объектов Function<Domain, Range>. Я не уверен, что имя шаблона действительно подходит, но это не Visitor. Он привносит функциональный стиль понимания списка в мир OO, где функции не являются естественно первоклассными объектами.

+0

Ясно, что я должен признать, что я не понимаю необходимость метода accept() и этого механизма двойной отправки. Какая польза от нас? – cibercitizen1

+0

Я исправляю себя. Я вижу, что это хитрость, чтобы проверенные элементы проверялись другой функцией в зависимости от ее типа. Но мне не кажется очень чистым, чтобы мне пришлось менять визиты с помощью метода accept. Кроме того, это необязательно, если все элементы в коллекциях действительно одного и того же типа. – cibercitizen1

+0

@donroby Является ли новая версия шаблоном посетителя? (Смотри ниже). – cibercitizen1

0

как о

for(String s : words) 
     System.out.println (s.toUpperCase()); 

    int total = 0; 
    for(String s : words) 
     total = total + s.length(); 
    System.out.println (" sum of lengths = " + total); 
+0

Упрощение лучше, но меня интересовал шаблон посетителя. Благодарю. – cibercitizen1