LINUX.ORG.RU

Visitor Pattern - почему так не принято его реализовать?

 , ,


0

1

Обычно делают так:

package org.ovk.javalearning;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        Structure s = new Structure();
        s.visit();
    }
}

class Visitor {
    void visit(IntElement element) {
        System.out.println("int: " + element.value);
    }
    void visit(StringElement element) {
        System.out.println("string: " + element.value);
    }
}

interface GenericElement {
    void accept(Visitor visitor);
}

class IntElement implements GenericElement {
    public int value = 0;

    IntElement(int value) {
        this.value = value;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class StringElement implements GenericElement {
    public String value = "";

    StringElement(String value) {
        this.value = value;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Structure {
    ArrayList<GenericElement> elements;
    Structure() {
        elements = new ArrayList<GenericElement>();
        elements.add(new IntElement(10));
        elements.add(new StringElement("Hello!"));
    }
    void visit() {
        Visitor visitor = new Visitor();
        for(GenericElement element : elements) {
            element.accept(visitor);
        }
    }
}

А почему не делают так?

package org.ovk.javalearning;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        Structure s = new Structure();
        s.visit();
    }
}

interface GenericElement {

}

class IntElement implements GenericElement {
    public int value = 0;

    IntElement(int value) {
        this.value = value;
    }
}

class StringElement implements GenericElement {
    public String value = "";

    StringElement(String value) {
        this.value = value;
    }
}

class Structure {
    ArrayList<GenericElement> elements;
    Structure() {
        elements = new ArrayList<GenericElement>();
        elements.add(new IntElement(10));
        elements.add(new StringElement("Hello!"));
    }
    void visit() {
        for(GenericElement element: elements) {
            if(element instanceof IntElement)
                System.out.println("int: " + ((IntElement) element).value);
            else if (element instanceof StringElement)
                System.out.println("string: " + ((StringElement) element).value);
        }
    }
}


★★★

как я понимаю тут теряется вся соль двойной диспетчеризации (https://en.wikipedia.org/wiki/Double_dispatch) что, вероятно, не главная добродетель этого шаблона, но несомненно - очень важная.

anonymous ()

Потому что у вас там не visitor, а тупой итератор.

belous_k_a ()
Ответ на: комментарий от anonymous

как я понимаю тут теряется вся соль двойной диспетчеризации

Да, согласен. Ну это я просто некрасиво написал. Меня больше интересует не это, а необходимость нагружать каждый класс элемента методом accept(). Вот если сделать так:

package org.ovk.javalearning;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        Structure s = new Structure();
        Visitor visitor = new Visitor();
        s.visit(visitor);
    }
}

class Visitor {
    void visit(GenericElement element) {
        if(element instanceof IntElement)
            System.out.println("int: " + ((IntElement) element).value);
        else if(element instanceof StringElement)
            System.out.println("string: " + ((StringElement) element).value);
    }
}

abstract class GenericElement {
}

class IntElement extends GenericElement {
    public int value = 0;

    IntElement(int value) {
        this.value = value;
    }
}

class StringElement extends GenericElement {
    public String value = "";

    StringElement(String value) {
        this.value = value;
    }
}

class Structure {
    ArrayList<GenericElement> elements;
    Structure() {
        elements = new ArrayList<GenericElement>();
        elements.add(new IntElement(10));
        elements.add(new StringElement("Hello!"));
    }
    void visit(Visitor visitor) {
        for(GenericElement element : elements) {
            visitor.visit(element);
        }
    }
}

Так вроде бы и двойная диспетчеризация остается, и нет необходимости в accept(), или я торможу?

ovk48 ★★★ ()
Ответ на: комментарий от ovk48

Меня больше интересует не это, а необходимость нагружать каждый класс элемента методом accept()

1. Последовательность if-ов медленнее двойной диспетчеризации при большом количестве классов.

2. Вот ты добавишь ты новый потомок GenericElement, а visit-ы забудешь поменять и компилятор тебе не поможет в поиске мест, о которых ты забыл.

Begemoth ★★★★★ ()

Если используешь instanceof и приведение типа, то, вероятнее всего, что-то делаешь не так.

baverman ★★★ ()
Ответ на: комментарий от ovk48

Так вроде бы и двойная диспетчеризация остается

это скорее одинарная диспетчеризация на основе if-ов, что как правильно заметили - признак (но не гарантия) того, что ты что-то делаешь не так.

anonymous ()
Ответ на: комментарий от ovk48

Наследование в ООП как раз для того и придумали, чтобы не было лишних приведений типов.

Если считаешь, что это всё не нужно, то можешь оказаться и от объектов вообще. «Ведь и без них можно всё сделать».

KivApple ★★★★★ ()
Ответ на: комментарий от KivApple

Наследование в ООП как раз для того и придумали, чтобы не было лишних приведений типов.

Тут многие бы поспорили.

Если считаешь, что это всё не нужно

Так про наследование как раз многие авторитетные мужики уже говорят, что оно не нужно.

ovk48 ★★★ ()
Ответ на: комментарий от anonymous

это скорее одинарная диспетчеризация на основе if-ов, что как правильно заметили - признак (но не гарантия) того, что ты что-то делаешь не так.

Ну наверно да. В общем, я согласен с мнением, что вариант с акцептами более идеологически чистый.

ovk48 ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.