views:

136

answers:

3

I have a class hierarchy as such:

        +-- VirtualNode
        |
INode --+                  +-- SiteNode
        |                  |
        +-- AbstractNode --+
                           |
                           +-- SiteSubNode

And a corresponding NodeCollection class that is build on INode. In order to display a NodeCollection I need to know the final type of each member. So I need a function like this

foreach (INode n in myNodeCollection)
{
    switch(n.GetType())
    {
        case(typeof(SiteNode)):
        // Display n as SiteNode
    }
}

Now, this is really not an object oriented way of doing it. Are there any patterns or recommended ways of doing the same thing, in your opinion?

EDIT
I already thought of adding a Display or Render method to the INode interface. That has the side effect of coupling the view to the model, which I would really like to avoid.

+1  A: 

What you're after is the visitor pattern, I think.

Andrew
Probably http://c2.com/cgi/wiki?HierarchicalVisitorPattern would be even better?
Sklivvz
Perhaps. I've only ever used the traditional visitor pattern. I'll certainly spend time looking into this though.
Andrew
+1  A: 

Polymorphism:

When ever you have a select statement using the type of an object, it is a prime candidate for refactoring to polymorphism.

Check out the book Refactoring by Martin Fowler:

"One of the most obvious symptoms of object-oriented code is its comparative lack of switch (or case) statements. The problem with switch statements is essentially that of duplication. Often you find the same switch statement scattered about a program in different places. If you add a new clause to the switch, you have to find all these switch, statements and change them. The objectoriented notion of polymorphism gives you an elegant way to deal with this problem.

Most times you see a switch statement you should consider polymorphism. The issue is where the polymorphism should occur. Often the switch statement switches on a type code. You want the method or class that hosts the type code value. So use Extract Method to extract the switch statement and then Move Method to get it onto the class where the polymorphism is needed. At that point you have to decide whether to Replace Type Code with Subclasses or Replace Type Code with State/Strategy. When you have set up the inheritance structure, you can use Replace Conditional with Polymorphism."

Here is one approach to using polymorphism in your situation:

  1. Define an abstract method in AbstractNode named something like Display().

  2. Then actually implement Display() in each of the SiteNode and SiteSubNode classes.

  3. Then, when you need to display these nodes, you could simply iterate through a collection containing items of type AbstractNode and call Display() for each.

  4. The call to Display() will automatically resolve to the actual concrete implementation for the real type of that item.

  5. Note: You could also move the Display() method from AbstractNode to the INode interface if VirtualNode is to be displayed.

Ash
I don't want to do that because it would couple the view to the model!
Sklivvz
How so? Which classes in your example are view and which are model? You do realise that simply by using inheritence you are automatically coupling the child to the parent class? Polymorphism or not.
Ash
Like I said,use the Visitor pattern. Define an INodeVisitor interface and create a NodeDisplayer class that implements that interface. Use the visitor pattern to decouple the node hierarchy from the classes that refer to the nodes.
Andrew
Andrew, for simplicity having a Display() method in each class is still perfectly viable approach. Display() could still quite happily delegate to some other UI oriented object. The Visitor pattern is overkill in simpler situations.
Ash
A: 

If you can change the INode interface - add a virtual method that returns the "view" and override it in the inherited classes.

If you can't change the base interface - implement extension methods for each of the classes and have them return the "view" of for each particular class.

Franci Penov