Вопросы по шаблону посетителя (пример на Java) - PullRequest
6 голосов
/ 06 июня 2011

Я просто пытаюсь понять основные преимущества использования шаблона посетителя.

Вот пример реализации Java

///////////////////////////////////
// Interfaces
interface MamalVisitor {
    void visit(Mammal mammal);
}
interface MammalVisitable {
    public void accept(MamalVisitor visitor);
}
interface Mammal extends MammalVisitable {
    public int getLegsNumber();
}
///////////////////////////////////


///////////////////////////////////
// Model
class Human implements Mammal {
    @Override
    public void accept(MamalVisitor visitor) {  visitor.visit(this);  }
    @Override
    public int getLegsNumber() { return 2; }
}
//PIRATE HAS A WOOD LEG
class Pirate extends Human { 
    @Override
    public int getLegsNumber() { return 1; }
    public int getWoodLegNumber() { return 1; }
}
class Dog implements Mammal {
    @Override
    public void accept(MamalVisitor visitor) {  visitor.visit(this);  }
    @Override
    public int getLegsNumber() { return 4; }
}
///////////////////////////////////


///////////////////////////////////
class LegCounterVisitor implements MamalVisitor {
    private int legNumber = 0;
    @Override
    public void visit(Mammal mammal) {   legNumber += mammal.getLegsNumber();   }
    public int getLegNumber() { return legNumber; }
}
class WoodLegCounterVisitor implements MamalVisitor {
    private int woodLegNumber = 0;
    @Override
    public void visit(Mammal mammal) {   
        // perhaps bad but i'm lazy
        if ( mammal instanceof Pirate ) {
            woodLegNumber += ((Pirate) mammal).getWoodLegNumber();
        }
    }
    public int getWoodLegNumber() { return woodLegNumber; }
}
///////////////////////////////////



///////////////////////////////////
public class Main {
    public static void main(String[] args) {
        // Create a list with 9 mammal legs and 3 pirate woodlegs
        List<Mammal> mammalList = Arrays.asList(
                new Pirate(),
                new Dog(),
                new Human(),
                new Pirate(),
                new Pirate()
        );

        ///////////////////////////////////
        // The visitor method
        LegCounterVisitor legCounterVisitor = new LegCounterVisitor();
        WoodLegCounterVisitor woodLegCounterVisitor = new WoodLegCounterVisitor();
        for ( Mammal mammal : mammalList ) {
            mammal.accept(legCounterVisitor);
            mammal.accept(woodLegCounterVisitor);
            // why not also using:
            // legCounterVisitor.visit(mammal);
            // woodLegCounterVisitor.visit(mammal);
        }
        System.out.println("Number of legs:" + legCounterVisitor.getLegNumber());
        System.out.println("Number of wood legs:" + woodLegCounterVisitor.getWoodLegNumber());

        ///////////////////////////////////
        // The standart method
        int legNumber = 0;
        int woodLegNumber = 0;
        for ( Mammal mammal : mammalList ) {
            legNumber += mammal.getLegsNumber();
            // perhaps bad but i'm lazy
            if ( mammal instanceof Pirate ) {
                woodLegNumber += ((Pirate) mammal).getWoodLegNumber();
            }
        }
        System.out.println("Number of legs:" + legNumber);
        System.out.println("Number of wood legs:" + woodLegNumber);
    }
}
///////////////////////////////////

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

С Apache Commons, или функциональным языком, классический способ, кажется, выполняет некоторую операцию отображения / уменьшения (отображение на номера ветвей и уменьшение с добавлением), и это довольно просто ...

Мне также интересно, почему мы используем

        mammal.accept(legCounterVisitor);
        mammal.accept(woodLegCounterVisitor);

а не

        legCounterVisitor.visit(mammal);
        woodLegCounterVisitor.visit(mammal);

Второй вариант, по-видимому, удаляет метод accept (...) для модельной детали.

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

Итак, мои вопросы: - Видите ли вы в моем примере преимущество использования посетителей? - если нет, можете ли вы предоставить конкретные примеры использования для посетителей? - полезны ли посетители на функциональных языках программирования

Единственный пример, который я нашел релевантным для этого шаблона, это случай симпатичного принтера, где вы сохраняете в состоянии посетителя смещение, которое будет использоваться при посещении различных узлов (для отображения дерева XML для примера)

Ответы [ 3 ]

5 голосов
/ 06 июня 2011

Шаблон посетителя просто двойная отправка .

Я не уверен, что согласен с вашей реализацией посетителя. Я бы реализовал что-то вроде этого:

interface MammalVisitor {
    void visit(Pirate pirate);
    void visit(Human human);
    void visit(Dog dog);
}

// Basic visitor provides no-op behaviour for everything.
abstract class MammalAdapter implements MammalVisitor {
    void visit(Pirate pirate) {};
    void visit(Human human) {};
    void visit(Dog dog) {};
}

И тогда реализация станет чище:

// We only want to provide specific behaviour for pirates
class WoodLegCounterVisitor extends MammalAdaptor {
    private int woodLegNumber = 0;
    @Override
    public void visit(Pirate pirate) {   
        woodLegNumber += pirate.getWoodLegNumber();
    }

    public int getWoodLegNumber() { return woodLegNumber; }
}

В ответ на ваш актуальный вопрос, главное преимущество использования посетителя состоит в том, чтобы избежать необходимости делать проверки «instanceof». Это дает вам возможность выделить логику для обработки иерархии в отдельный класс. Это также дает вам возможность добавлять новое поведение без изменения исходных классов.

4 голосов
/ 06 июня 2011

Шаблон посетителя - это необычная система переключения регистра / соответствия шаблонов для облегчения обхода графика .

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

Даже в JAVA, с instanceof или с использованием enum, посетитель - более причудливый способ выполнения задач, чем универсальное решение, так как многие алгоритмы не вписываются в него.

3 голосов
/ 06 июня 2011

Цель Шаблон посетителя состоит в том, чтобы отделить структуру объекта (в вашем случае, Mammal) от алгоритма (в вашем случае, алгоритм счетчика ноги счетчика).* Вся идея в том, что ваш объект (в основном в Java, JavaBeans) вообще не меняет свою структуру, и только новая виртуальная функция вводится для введения нового алгоритма.В отличие от реализации Jeff Foster , можно сделать Generics, чтобы сделать код проще.Это привносит специфичность вашему посетителю, например:

public interface MammalVisitor<T extends Mammal> {

    public void visit(T mammal);
}

public class LegCounterVisitor implements MamalVisitor<Human> {
    private int legNumber = 0;
    @Override
    public void visit(Human mammal) {   legNumber += mammal.getLegsNumber();   }
    public int getLegNumber() { return legNumber; }
}

public class WoodLegCounterVisitor implements MamalVisitor<Pirate> {
    private int legNumber = 0;
    @Override
    public void visit(Pirate mammal) {legNumber += mammal.getWoodLegNumber();   }
    public int getLegNumber() { return legNumber; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...