Шаблон посетителя, уберите необходимость в касте - PullRequest
3 голосов
/ 23 марта 2009

У меня есть вопрос относительно шаблона посетителя, в настоящее время у меня есть две сборки. Моя первая сборка содержит несколько интерфейсов.

public interface INode
{
    void Visit(INodeVisitor visitor);
}

public interface INodeVisitor
{
    void VisitContainer(IContainer container);
}

public interface IContainer : INode
{
}

И моя вторая сборка

    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);
    }
}

Что я хочу сделать, так это избежать необходимости приведения в классе ContainerVisitor, я хочу ссылаться на Контейнер напрямую. Я не могу изменить интерфейс интерфейса INodeVisitor для использования контейнера. Есть идеи? Должен ли я просто разыграть?

Приветствия

Rohan

Ответы [ 3 ]

3 голосов
/ 23 марта 2009

Приведение неизбежно, но вы можете немного абстрагировать его, чтобы удалить из фактического класса ContainerVisitor.

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);
}

Теперь ContainerVisitor может происходить от NodeVisitor и избегать приведения

public class ContainerVisitor : NodeVisitor<Container>{
    protected override void VisitContainerTyped(Container container){
        // Do some stuff...
    }
}
1 голос
/ 23 марта 2009

Если вы не сможете определить свой интерфейс IContainer лучше, вы не сможете избежать приведения. И, как говорит Марк, это побеждает цель абстракции на основе интерфейса.

public interface IContainer : INode
{
    void Add(IComponent component);
    void Add(IComponent component, string name);
    void Remove(IComponent component);
    ComponentCollection Components { get; }
}

С более четко определенным IContainer вашим посетителям не нужно будет выполнять кастинг.

public class ContainerVisitor : INodeVisitor
{
    public void VisitContainer(IContainer container)
    {
        foreach (IComponent component in container.Components)
        {
            // ...
        }
    }
}
1 голос
/ 23 марта 2009

Я не думаю, что допустимо , чтобы предположить, что это Container экземпляр. Я вполне мог бы написать свою собственную реализацию IContainer, и ваша реализация захлебнулась бы ею, нарушив весь смысл абстракции на основе интерфейса. Вы также не можете (действительно) просто изменить API для принятия Container (используя явную реализацию для поддержки IContainer), поскольку я мог бы использовать интерфейс, а не класс:

INodeVisitor visitor = new ContainerVisitor();
visitor.VisitContainer(myBespokeContainer);

Если вам нужна дополнительная поддержка чего-либо из базового класса, вам придется использовать «как» для ее обнаружения. Что-нибудь еще, и вы нарушаете абстракцию.

...