Design Patterns — Parte 25 — Visitor
Intenção
É um padrão de design comportamental que permite separar algoritmos dos objetos nos quais eles operam.
Problema
Muitas operações distintas e não relacionadas precisam ser executadas em objetos de nó em uma estrutura agregada heterogênea. Você deseja evitar “poluir” as classes de nós com essas operações. E não é necessário consultar o tipo de cada nó e converter o ponteiro no tipo correto antes de executar a operação desejada.
Solução
O padrão Visitor sugere que você coloque o novo comportamento em uma classe separada chamada visitor , em vez de tentar integrá-lo às classes existentes. O objeto original que tinha que executar o comportamento agora é passado para um dos métodos do visitante como argumento, fornecendo ao método acesso a todos os dados necessários contidos no objeto.
Implementação
O diagrama de classes UML para a implementação do Visitor Design Pattern é fornecido abaixo:
Prós
- Princípio Aberto / Fechado . Você pode introduzir um novo comportamento que possa trabalhar com objetos de diferentes classes sem alterar essas classes.
- Princípio de responsabilidade única . Você pode mover várias versões do mesmo comportamento para a mesma classe.
- Um objeto visitante pode acumular algumas informações úteis ao trabalhar com vários objetos. Isso pode ser útil quando você deseja percorrer alguma estrutura complexa de objetos, como uma árvore de objetos, e aplicar o visitante a cada objeto dessa estrutura.
Contras
- Você precisa atualizar todos os visitantes cada vez que uma classe é adicionada ou removida da hierarquia de elementos.
- Os visitantes podem não ter acesso necessário aos campos e métodos particulares dos elementos com os quais devem trabalhar.
Exemplo
Uso do padrão
O que é oque?
Client
Esta é uma classe que tem acesso aos objetos da estrutura de dados e pode instruí-los a aceitar um Visitante para executar as operações apropriadas.
ObjectStructure
Esta é uma classe que contém todos os elementos que podem ser usados pelos visitantes.
Element
Essa é uma interface que especifica a operação Accept.
ConcreteElement
Essas são classes que implementam a interface Element e mantêm as informações reais.
Visitor
Essa é uma interface que especifica as operações de Visita para visitantes concretos.
ConcreteVisitor
Essas são subclasses que implementam a interface do visitante.
Exemplo prático
Bom vamos para o nosso exemplo prático, vou começar criando a interface IElement, que irá conter o seguinte código:
public interface IElement
{
void Accept(IVisitor visitor);
}
Em seguida irei criar duas classes concretas que irão implementar nosso IElement
- ConcreteElementA
public class ConcreteElementA : IElement
{
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementA(this);
}public string ExclusiveMethodOfConcreteElementA()
{
return "A";
}
}
- ConcreteElementB
public class ConcreteElementB : IElement
{
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementB(this);
}public string SpecialMethodOfConcreteElementB()
{
return "B";
}
}
Em seguida iremos criar uma interface IVisitor, que irá conter o seguinte código:
public interface IVisitor
{
void VisitConcreteElementA(ConcreteElementA element);void VisitConcreteElementB(ConcreteElementB element);
}
Em seguida iremos criar nossas classes concretas que implementam a interface IVisitor
- ConcreteVisitor1
class ConcreteVisitor1 : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA element)
{
Console.WriteLine($"{element.ExclusiveMethodOfConcreteElementA()} ConcreteVisitor1");
}public void VisitConcreteElementB(ConcreteElementB element)
{
Console.WriteLine($"{element.SpecialMethodOfConcreteElementB()} ConcreteVisitor1");
}
}
- ConcreteVisitor2
class ConcreteVisitor2 : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA element)
{
Console.WriteLine($"{element.ExclusiveMethodOfConcreteElementA()} ConcreteVisitor2");
}public void VisitConcreteElementB(ConcreteElementB element)
{
Console.WriteLine($"{element.SpecialMethodOfConcreteElementB()} ConcreteVisitor2");
}
}
Em seguida iremos criar nossa classe Client, que irá conter o seguinte código:
e por fim vamos alterar nossa classe Program.cs, que será nossa ObjectStructure, que irá conter o seguinte código:
class Program
{
static void Main(string[] args)
{
List<IElement> components = new List<IElement>
{
new ConcreteElementA(),
new ConcreteElementB()
};Console.WriteLine("The client code works with all visitors via the base Visitor interface:");
var visitor1 = new ConcreteVisitor1();
Client.ClientCode(components, visitor1);Console.WriteLine();Console.WriteLine("It allows the same client code to work with different types of visitors:");
var visitor2 = new ConcreteVisitor2();
Client.ClientCode(components, visitor2);Console.WriteLine("Press any key to continue!");
Console.ReadKey();
}
}
Bom pessoal, esse é nosso último artigo sobre design patterns do GOF, em breve irei começar a falar de design patterns que estou usando nas novas arquiteturas para nuvem, e também pretendo falar sobre os padrões do GRASP, espero que tenham gostado da série, que no total foi de 25 artigos.
Lembrando que todo o código está em meu github se tiver qualquer dúvida pode comentar, que eu irei tentar responder. Abraços e até mais.