Шаблон дизайна посетителя в Java

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

1. обзор

В этом руководстве мы познакомим вас с одним из поведенческих шаблонов проектирования GoF - посетителем.

Сначала мы объясним его цель и проблему, которую он пытается решить.

Далее мы рассмотрим UML-диаграмму Visitor и реализацию практического примера.

2. Шаблон дизайна посетителя

Целью шаблона Visitor является определение новой операции без внесения изменений в существующую структуру объекта.

Представьте, что у нас есть ссылка: / java-complex-pattern object, который состоит из компонентов. Структура объекта фиксированная - мы либо не можем ее изменить, либо не планируем добавлять в структуру новые типы элементов.

Теперь, как мы можем добавить новую функциональность в наш код без изменения существующих классов?

Шаблон дизайна посетителя может быть ответом. Проще говоря,we’ll have to do is to add a function which accepts the visitor class to each element of the structure.

Таким образом, наши компоненты позволят посетителю «посетить» их и выполнить любые необходимые действия с этим элементом.

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

Следовательно,we’ll make good use of the Open/Closed principle as, мы не будем изменять код, но мы все равно сможем расширить функциональность, предоставив новую упрощеннуюVisitor .

3. Диаграмма UML

image

На приведенной выше диаграмме UML у нас есть две иерархии реализации: специализированные посетители и конкретные элементы.

Прежде всего, клиент использует реализацию Visitor и применяет ее к структуре объекта. Составной объект перебирает свои компоненты и применяет посетителя к каждому из них.

Теперь особенно актуально то, чтоconcrete elements (ConcreteElementA and ConcreteElementB) are accepting a Visitor, simply allowing it to visit them.

Наконец, этот метод одинаков для всех элементов в структуре, он выполняетdouble dispatch с передачей себя (через ключевое словоthis) методу посещения посетителя.

4. Реализация

Нашим примером будет пользовательский объектDocument object, состоящий из конкретных элементов JSON и XML; элементы имеют общий абстрактный суперклассElement.

КлассDocument:

public class Document extends Element {

    List elements = new ArrayList<>();

    // ...

    @Override
    public void accept(Visitor v) {
        for (Element e : this.elements) {
            e.accept(v);
        }
    }
}

КлассElement имеет абстрактный метод, который принимает синтерфейсVisitor :

public abstract void accept(Visitor v);

Следовательно, при создании нового элемента назовите егоJsonElement, нам нужно будет предоставить реализацию этого метода.

Однако из-за характера шаблона Visitor реализация будет такой же, поэтому в большинстве случаев нам потребуется скопировать и скопировать стандартный шаблон из другого, уже существующего элемента:

public class JsonElement extends Element {

    // ...

    public void accept(Visitor v) {
        v.visit(this);
    }
}

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

Поэтому у нашего посетителя будет отдельный метод для данного типа:

public class ElementVisitor implements Visitor {

    @Override
    public void visit(XmlElement xe) {
        System.out.println(
          "processing an XML element with uuid: " + xe.uuid);
    }

    @Override
    public void visit(JsonElement je) {
        System.out.println(
          "processing a JSON element with uuid: " + je.uuid);
    }
}

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

Это дает нам доступ к конкретному объекту структуры, над которой мы можем выполнять необходимые действия.

5. тестирование

Для тестирования давайте взглянем наVisitorDemoclass:

public class VisitorDemo {

    public static void main(String[] args) {

        Visitor v = new ElementVisitor();

        Document d = new Document(generateUuid());
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new XmlElement(generateUuid()));

        d.accept(v);
    }

    // ...
}

Сначала мы создаемElementVisitor, он содержит алгоритм, который мы будем применять к нашим элементам.

Затем мы настраиваемDocument w с соответствующими компонентами и применяем посетителя, который будет приниматься каждым элементом структуры объекта.

Вывод будет выглядеть так:

processing a JSON element with uuid: fdbc75d0-5067-49df-9567-239f38f01b04
processing a JSON element with uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e
processing an XML element with uuid: 091bfcb8-2c68-491a-9308-4ada2687e203

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

6. Downsides

Как и у каждого шаблона проектирования, даже у Посетителя есть свои недостатки, в частности, его использование makes it more difficult to maintain the code if we need to add new elements to the object’s structure.

Например, если мы добавляем новыйYamlElement,, тогда нам нужно обновить всех существующих посетителей с помощью нового метода, необходимого для обработки этого элемента. После этого, если у нас будет десять или более конкретных посетителей, обновлять их всех может быть обременительно.

Кроме этого, при использовании этого шаблона бизнес-логика, связанная с одним конкретным объектом, распространяется на все реализации посетителя.

7. Заключение

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

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

Кроме того, Посетитель может в конечном итоге агрегировать данные на основе элемента, который он пересекает.

Чтобы увидеть более специализированную версию шаблона проектирования Visitor, ознакомьтесь сvisitor pattern in Java NIO - использование шаблона в JDK.

Как обычно, полный код доступен наGithub project.