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

Foi útil?

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)
        {
            // ...
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top