Padrão de Design do Visitante em Java

Padrão de Design do Visitante em Java

1. Visão geral

Neste tutorial, apresentaremos um dos padrões de design comportamentais do GoF - o Visitante.

Primeiro, vamos explicar seu propósito e o problema que ele tenta resolver.

A seguir, daremos uma olhada no diagrama UML do visitante e na implementação do exemplo prático.

2. Padrão de design do visitante

O objetivo de um padrão Visitor é definir uma nova operação sem introduzir as modificações em uma estrutura de objeto existente.

Imagine que temos um link: / java-composite-pattern objeto que consiste em componentes. A estrutura do objeto é fixa - não podemos alterá-la ou não planejamos adicionar novos tipos de elementos à estrutura.

Agora, como poderíamos adicionar novas funcionalidades ao nosso código sem modificar as classes existentes?

O padrão de design do visitante pode ser uma resposta. Simplificando,we’ll have to do is to add a function which accepts the visitor class to each element of the structure.

Dessa forma, nossos componentes permitirão que a implementação do visitante os "visite" e execute qualquer ação necessária nesse elemento.

Em outras palavras, vamos extrair o algoritmo que será aplicado à estrutura do objeto das classes.

Consequentemente,we’ll make good use of the Open/Closed principle as não modificaremos o código, mas ainda seremos capazes de estender a funcionalidade, fornecendo uma nova simplificaçãoVisitor .

3. Diagrama UML

image

No diagrama UML acima, temos duas hierarquias de implementação, visitantes especializados e elementos concretos.

Primeiro de tudo, o cliente usa uma implementação Visitor e a aplica à estrutura do objeto. O objeto composto itera sobre seus componentes e aplica o visitante a cada um deles.

Agora, especialmente relevante é queconcrete elements (ConcreteElementA and ConcreteElementB) are accepting a Visitor, simply allowing it to visit them.

Por último, este método é o mesmo para todos os elementos na estrutura, ele executadouble dispatch passando-se (por meio da palavra-chavethis) para o método de visita do visitante.

4. Implementação

Nosso exemplo será customDocument object que consiste em elementos concretos JSON e XML; os elementos têm uma superclasse abstrata comum, oElement.

A classeDocument:

public class Document extends Element {

    List elements = new ArrayList<>();

    // ...

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

A classeElement tem um método abstrato que aceita a sinterfaceVisitor :

public abstract void accept(Visitor v);

Portanto, ao criar o novo elemento, chame-o deJsonElement, teremos que fornecer a implementação desse método.

No entanto, devido à natureza do padrão Visitor, a implementação será a mesma; portanto, na maioria dos casos, seria necessário copiar e colar o código padrão de outro elemento já existente:

public class JsonElement extends Element {

    // ...

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

Uma vez que nossos elementos permitem visitá-los por qualquer visitante, digamos que queremos processar nossasDocument eleções, mas cada uma de uma maneira diferente, dependendo do tipo de classe.

Portanto, nosso visitante terá um método separado para o tipo especificado:

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

Aqui, nosso visitante concreto implementa dois métodos, correspondentemente um para cada tipo deElement.

Isso nos dá acesso ao objeto específico da estrutura na qual podemos executar as ações necessárias.

5. Teste

Para fins de teste, vamos dar uma olhada emVisitorDemoclass:

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

    // ...
}

Primeiro, criamos umElementVisitor, ele contém o algoritmo que aplicaremos aos nossos elementos.

Em seguida, configuramos nossoDocument com os componentes apropriados e aplicamos o visitante que será aceito por todos os elementos de uma estrutura de objeto.

A saída seria assim:

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

Ele mostra que o visitante visitou cada elemento de nossa estrutura, dependendo do tipoElement , ele despachou o processamento para o método apropriado e pode recuperar os dados de cada objeto subjacente.

6. Desvantagens

Como cada padrão de design, até mesmo o Visitante tem suas desvantagens, particularmente, seu uso makes it more difficult to maintain the code if we need to add new elements to the object’s structure.

Por exemplo, se adicionarmos novosYamlElement,, precisamos atualizar todos os visitantes existentes com o novo método desejado para processar este elemento. Depois disso, se tivermos dez ou mais visitantes concretos, pode ser complicado atualizar todos eles.

Fora isso, ao usar esse padrão, a lógica de negócios relacionada a um objeto específico se espalha por todas as implementações de visitantes.

7. Conclusão

O padrão Visitor é ótimo para separar o algoritmo das classes nas quais ele opera. Além disso, facilita a adição de novas operações, apenas fornecendo uma nova implementação do Visitor.

Além disso, não dependemos das interfaces dos componentes e, se forem diferentes, tudo bem, pois temos um algoritmo separado para processamento por elemento concreto.

Além disso, o Visitante pode eventualmente agregar dados com base no elemento que ele atravessa.

Para ver uma versão mais especializada do padrão de design do Visitante, verifiquevisitor pattern in Java NIO - o uso do padrão no JDK.

Como de costume, o código completo está disponível emGithub project.