Padrão visitante, eliminar a necessidade de elenco
-
21-08-2019 - |
Pergunta
Eu tenho uma pergunta sobre o padrão do visitante, eu tenho atualmente dois conjuntos. Minha primeira montagem contém várias interfaces.
public interface INode
{
void Visit(INodeVisitor visitor);
}
public interface INodeVisitor
{
void VisitContainer(IContainer container);
}
public interface IContainer : INode
{
}
E a minha segunda montagem
class Program
{
static void Main(string[] args)
{
ContainerVisitor visitor = new ContainerVisitor();
visitor.VisitContainer(new Container());
}
}
public class ContainerVisitor : INodeVisitor
{
public void VisitContainer(IContainer value)
{
Container container = value as Container;
// Do some stuff...
}
}
public class Container : IContainer
{
public void Visit(INodeVisitor visitor)
{
visitor.VisitContainer(this);
}
}
O que eu quero fazer é evitar a necessidade de elenco na classe ContainerVisitor, quero fazer referência a Container diretamente. Eu não posso mudar a interface interface de INodeVisitor usar Container. Alguma ideia? Eu deveria lançar?
Felicidades
Rohan
Solução
O elenco é inevitável, mas você poderia abstrato-lo um pouco para removê-lo da classe ContainerVisitor real.
public class NodeVisitor<T> : INodeVisitor where T : IContainer {
public void VisitContainer(T node) {
var container = node as T;
if ( container != null ) {
VisitTyped(container);
}
}
protected abstract VisitContainerTyped(T container);
}
Agora ContainerVisitor pode derivar de NodeVisitor e evitar o elenco
public class ContainerVisitor : NodeVisitor<Container>{
protected override void VisitContainerTyped(Container container){
// Do some stuff...
}
}
Outras dicas
Eu não acho que é válido para assumir que é uma instância Container
. Eu poderia, perfeitamente válida, escrever a minha própria implementação IContainer
, e sua implementação iria sufocá-lo, quebrando todo o ponto de abstração baseada em interface. Você também não pode (legitimamente) basta mudar a API para aceitar Container
(usando a implementação explícita para IContainer
apoio), desde que eu poderia estar usando a interface em vez da classe:
INodeVisitor visitor = new ContainerVisitor();
visitor.VisitContainer(myBespokeContainer);
Se você quiser suporte opcional para algo a partir da base de classe, então você vai ter que usar "como" para detectá-lo. Qualquer outra coisa e você está quebrando a abstração.
A menos que você pode definir sua interface IContainer melhor, então você não será capaz de evitar o elenco. E como diz Marc, que derrota o propósito da abstração baseada na interface.
public interface IContainer : INode
{
void Add(IComponent component);
void Add(IComponent component, string name);
void Remove(IComponent component);
ComponentCollection Components { get; }
}
Com o IContainer melhor definido, os visitantes vão não precisa fazer qualquer casting.
public class ContainerVisitor : INodeVisitor
{
public void VisitContainer(IContainer container)
{
foreach (IComponent component in container.Components)
{
// ...
}
}
}