Visitor em Go
O Visitor é um padrão de projeto comportamental que permite adicionar novos comportamentos à hierarquia de classes existente sem alterar nenhum código existente.
Leia por que os Visitors não podem ser simplesmente substituídos pela sobrecarga de método em nosso artigo Visitor e Double Dispatch.
Exemplo conceitual
O padrão Visitor permite adicionar comportamento a uma struct sem realmente modificar a struct. Digamos que você seja o mantenedor de uma biblioteca que tem structs de formatos diferentes, como:
- Quadrado
- Círculo
- Triângulo
Cada uma das structs de forma acima implementa a interface de forma comum.
Depois que as pessoas em sua empresa começaram a usar sua biblioteca incrível, você foi inundado com solicitações de recursos. Vamos revisar um dos mais simples: uma equipe solicitou que você adicionasse o comportamento getArea
às structs de forma.
Existem muitas opções para resolver este problema.
A primeira opção que vem à mente é adicionar o método getArea
diretamente na interface de forma e então implementá-lo em cada struct de forma. Esta parece ser a solução certa, mas tem um custo. Como mantenedor da biblioteca, você não quer arriscar quebrar seu precioso código toda vez que alguém solicitar outro comportamento. Ainda assim, você deseja que outras equipes estendam sua biblioteca de alguma forma.
A segunda opção é que a equipe que está solicitando o recurso pode implementar o comportamento por conta própria. Porém, nem sempre isso é possível, pois esse comportamento pode depender do código privado.
A terceira opção é resolver o problema acima usando o padrão Visitor. Começamos definindo uma interface de visitante como esta:
As funções visitForSquare (square)
, visitForCircle (circle)
, visitForTriangle (triangle)
nos permitem adicionar funcionalidade a quadrados, círculos e triângulos, respectivamente.
Está se perguntando por que não podemos ter um único método visit(shape)
na interface do visitante? O motivo é que a linguagem Go não oferece suporte à sobrecarga de método, então você não pode ter métodos com os mesmos nomes, mas com parâmetros diferentes.
Agora, a segunda parte importante é adicionar o método accept
à interface de forma.
Todas as structs de forma precisam definir este método, de forma semelhante a este:
Espere um segundo, eu não mencionei que não queremos modificar nossas structs de forma existentes? Infelizmente, sim, ao usar o padrão Visitor, temos que alterar nossas structs de forma. Mas essa modificação só será feita apenas uma vez.
No caso de adicionar qualquer outro comportamento, como getNumSides
, getMiddleCoordinates
, usaremos a mesma função accept(v visitor)
sem quaisquer mudanças adicionais nas structs de forma.
No final, as structs de forma precisam ser modificadas apenas uma vez, e todas as solicitações futuras para comportamentos diferentes podem ser tratadas usando a mesma função de aceitação. Se a equipe solicitar o comportamento getArea
, podemos simplesmente definir a implementação concreta da interface do visitante e escrever a lógica de cálculo da área nessa implementação concreta.