Общий шаблон посетителей в Java - PullRequest
0 голосов
/ 16 октября 2010

Является ли следующая 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 ]

6 голосов
/ 16 октября 2010

Это не шаблон посетителя .

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

Цитирование из раздела «Применимость» для посетителя в Шаблонах проектирования :

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

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

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

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

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

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

2 голосов
/ 16 октября 2010

Благодаря ответу 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);
    }
} // ()

}

0 голосов
/ 16 октября 2010

как насчет

    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);
...