Reputation: 9308
i have a question regarding the visitor pattern, I currently have two assemblies. My first assembly contains several interfaces.
public interface INode
{
void Visit(INodeVisitor visitor);
}
public interface INodeVisitor
{
void VisitContainer(IContainer container);
}
public interface IContainer : INode
{
}
And my second assembly
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);
}
}
What i want to do is avoid the need to cast in the ContainerVisitor class, I want to reference the Container directly. I cannot change the interface INodeVisitor interface to use Container. Any ideas? Should i just cast?
Cheers
Rohan
Upvotes: 3
Views: 927
Reputation: 3444
Unless you can define your IContainer interface better, then you won't be able to avoid the cast. And as Marc says, that defeats the purpose of the interface-based abstraction.
public interface IContainer : INode
{
void Add(IComponent component);
void Add(IComponent component, string name);
void Remove(IComponent component);
ComponentCollection Components { get; }
}
With the better defined IContainer, your Visitors won't need to do any casting.
public class ContainerVisitor : INodeVisitor
{
public void VisitContainer(IContainer container)
{
foreach (IComponent component in container.Components)
{
// ...
}
}
}
Upvotes: 1
Reputation: 1064324
I don't think it is valid to assume that it is a Container
instance. I could, perfectly validly, write my own IContainer
implementation, and your implementation would choke on it, breaking the whole point of interface-based abstraction. You also can't (validly) just change the API to accept Container
(using explicit implementation to support IContainer
), since I could be using the interface rather than the class:
INodeVisitor visitor = new ContainerVisitor();
visitor.VisitContainer(myBespokeContainer);
If you want optional support for something from the base-class, then you are going to have to use "as" to detect it. Anything else and you are breaking the abstraction.
Upvotes: 1
Reputation: 755587
The cast is unavoidable, but you could abstract it out a bit to remove it from the actual ContainerVisitor class.
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);
}
Now ContainerVisitor can derive from NodeVisitor and avoid the cast
public class ContainerVisitor : NodeVisitor<Container>{
protected override void VisitContainerTyped(Container container){
// Do some stuff...
}
}
Upvotes: 3