Contexto
Eu tenho usado com uma hierarquia de objetos (uma árvore de expressão) um padrão de visitante "pseudo" (pseudo, pois ele não usa expedição dupla):
public interface MyInterface
{
void Accept(SomeClass operationClass);
}
public class MyImpl : MyInterface
{
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
}
Esse design era, no entanto questionável, bastante confortável, pois o número de implementações do MyInterface é significativo (~ 50 ou mais) e eu não precisava adicionar operações extras.
Cada implementação é única (é uma expressão ou operador diferente) e algumas são compostas (por exemplo, nós do operador que conterão outros nós do operador / folha).
No momento, o Traversal é executado chamando a operação Accept no nó raiz da árvore, que por sua vez chama Accept em cada um dos nós filhos, que por sua vez ... e assim por diante ...
Mas chegou a hora de adicionar uma nova operação , como uma bonita impressão:
public class MyImpl : MyInterface
{
// Property does not come from MyInterface
public string SomeProperty { get; set; }
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
public void Accept(SomePrettyPrinter printer)
{
printer.PrettyPrint(this.SomeProperty);
}
}
Basicamente, vejo duas opções:
- Manter o mesmo design, adicionando um novo método para minha operação a cada classe derivada, às custas da manutenção (não uma opção, IMHO)
- Use o padrão Visitante "true", à custa da extensibilidade (não uma opção, pois espero ter mais implementações ao longo do caminho ...), com mais de 50 sobrecargas do método Visit, cada uma correspondendo a uma implementação específica ?
Questão
Você recomendaria usar o padrão Visitor? Existe algum outro padrão que possa ajudar a resolver esse problema?
fonte
MyInterface
.. todas essas classes têm uma implementação exclusiva deDoSomething
eDoSomethingElse
? Não vejo onde sua classe visitante realmente atravessa a hierarquia - que mais parece umfacade
no momento ..Respostas:
Eu tenho usado o padrão de visitantes para representar árvores de expressão ao longo de mais de 10 anos em seis projetos de larga escala em três linguagens de programação, e estou muito satisfeito com o resultado. Encontrei algumas coisas que tornaram a aplicação do padrão muito mais fácil:
Não use sobrecargas na interface do visitante
Coloque o tipo no nome do método, ou seja, use
ao invés de
Adicione um método "pegar desconhecido" à sua interface de visitante.
Isso tornaria possível para usuários que não podem modificar seu código:
Isso permitiria que eles criassem suas próprias implementações
IExpression
eIVisitor
que "entendessem" suas expressões usando informações de tipo de tempo de execução na implementação de seuVisitExpression
método catch-all .Forneça uma implementação
IVisitor
padrão da interface " faça nada"Isso permitiria que os usuários que precisam lidar com um subconjunto de tipos de expressão desenvolvessem seus visitantes mais rapidamente e tornassem o código imune a você adicionando mais métodos
IVisitor
. Por exemplo, escrever um visitante que colha todos os nomes de variáveis de suas expressões torna-se uma tarefa fácil, e o código não será interrompido, mesmo se você adicionar vários tipos de expressõesIVisitor
mais tarde.fonte
Do not use overloads in the interface of the visitor
?