Рубрики
Без рубрики

Шаблон посетителей на Java

Когда я нахожу концепцию трудной для понимания, я пытаюсь разоблачить ее, чтобы обнажить суть… Помеченный структурами данных, java, шаблонами проектирования, программированием.

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

Допустим, у нас есть три класса, производных от общего родителя, называемого

abstract class A
{
    public String name;
    abstract void accept(Visitor v);
}

класс B, содержащий два объекта в качестве компонентов:

class B extends A
{
    public A child1;
    public A child2;

    public B(String name)
    {
        this.name = name;
    }

[@Override](http://twitter.com/Override)
    void accept(Visitor v)
    {
        v.visitB(this);
    }
}

класс C, который имеет один компонент:

class C extends A
{
    public A child;

    public C(String name)
    {
        this.name = name;
    }

[@Override](http://twitter.com/Override)
    void accept(Visitor v)
    {
        v.visitC(this);
    }
}

и класс D, у которого нет компонентов

class D extends A
{
    public D(String name)
    {
        this.name = name;
    }

[@Override](http://twitter.com/Override)
    void accept(Visitor v)
    {
        v.visitD(this);
    }
}

Все три класса предоставляют свойство, имя, которое позволяет нам различать их экземпляры, и метод с именем accept, который позволяет посетителям посещать их. Классам все равно, и им не нужно знать, чем занимаются их посетители. Посетитель – это интерфейс:

interface Visitor
{
    public void visitB(B b);
    public void visitC(C c);
    public void visitD(D d);
}

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

class PrintVisitor implements Visitor
{
    public void visitB(B b)
    {
        b.child1.accept(this);
        System.out.println(b.name + " visited.");
        b.child2.accept(this);
    }

    public void visitC(C c)
    {
        System.out.println(c.name + " visited.");
        c.child.accept(this);
    }

    public void visitD(D d)
    {
        System.out.println(d.name + " visited.");
    }
}

Посетитель рекурсивен: он посещает узел дерева, а затем посещает его дочерние элементы. Теперь давайте создадим дерево, составленное из классов B, C и D:

        /\*
            F
          / \
        B G
      / \ \
     A D H
         / \ \
        C E I

        \*/

Существует девять объектов и семь отношений. Сначала создайте объекты:

        B f = new B("F");
        B b = new B("B");
        B d = new B("D");

        C g = new C("G");
        C h = new C("H");

        D a = new D("A");
        D c = new D("C");
        D e = new D("E");
        D i = new D("I");

Далее, отношения:

        f.child1 = b;
        f.child2 = g;

        b.child1 = a;
        b.child2 = d;

        d.child1 = c;
        d.child2 = e;

        g.child = h;
        h.child = i;

И, наконец, начните посещать свое дерево, посетив его корневой узел:

        PrintVisitor v = new PrintVisitor();
        f.accept(v);

Результат таков:

A visited.
B visited.
C visited.
D visited.
E visited.
F visited.
G visited.
H visited.
I visited.

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

class PrintVisitor implements Visitor
{
    public void visitB(B b)
    {
        System.out.println(b.name + " visited.");
        b.child1.accept(this);
        b.child2.accept(this);
    }

    public void visitC(C c)
    {
        System.out.println(c.name + " visited.");
        c.child.accept(this);
    }

    public void visitD(D d)
    {
        System.out.println(d.name + " visited.");
    }
}

Теперь результат таков:

F visited.
B visited.
A visited.
D visited.
C visited.
E visited.
G visited.
H visited.
I visited.

В обходе после заказа посетитель сначала посещает дочерние узлы и только затем отображает их имя:

class PrintVisitor implements Visitor
{
    public void visitB(B b)
    {
        b.child1.accept(this);
        b.child2.accept(this);
        System.out.println(b.name + " visited.");
    }

    public void visitC(C c)
    {
        c.child.accept(this);
        System.out.println(c.name + " visited.");
    }

    public void visitD(D d)
    {
        System.out.println(d.name + " visited.");
    }
}

Вот результат:

A visited.
C visited.
E visited.
D visited.
B visited.
I visited.
H visited.
G visited.
F visited.

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

  • Посещаемым объектам не нужно знать, что делают их посетители, им просто нужно принять их.
  • Должен существовать протокол, позволяющий взаимодействовать посещаемым объектам и посетителям, в нашем случае интерфейс посетителя.
  • Посетитель использует отдельные методы (т. е. посещение B, посещения и посещения для посещения каждого класса)

(Вы можете найти код на Github .)

Оригинал: “https://dev.to/vicentemaldonado/visitor-pattern-in-java-3lh1”