Search code examples
iteratorvisitor-pattern

Visitor Pattern VS Iterator Pattern: visiting across hierarchy class?


I'm studying Visitor Pattern advantages, and quoting Design Patterns:

But an iteratorcan't work across object structures with different types of elements. Forexample, the Iterator interface defined on page 295 can access only objects of type Item:

template <class Item> 
clas  Iterator { // ... Item CurrentItem() const; };

This implies that all elements the iterator can visit have a common parentclass Item. Visitor does not have this restriction...

class Visitor {
public:
// ...
void VisitMyType(MyType*);
void VisitYourType(YourType*);
};

MyType and YourType do not have to be related throughinheritance at all.

I agree about this quote, but I can't figure out an example where the Visitor Pattern could explore a structure (like a List) where objects collected in it are not related by a super class.

In other words, can you show me an example where the features above is true please?


Solution

  • First, you should know what these patterns are for.

    The Iterator Pattern is used to access an aggregate sequentially without exposing its underlying representation. So you could Hide a List or array or similar aggregates behind an Iterator.

    Visitor Pattern is used to perform an action on a structure of elements without changing the implementation of the elements themselves.

    So you use the patterns in two different situations and not as alternatives to each other.

    In the Visitor Pattern you implement an Interface IAcceptor in each element you want to visit. So the Visitor Pattern doesn't rely on a superclass but on Interfaces

    public interface IAcceptor
    {
        public void Accept(IVisitor visitor);
    }
    

    So if you have a List of objects you can iterate over it and visit the objects implementing IAcceptor

    public VisitorExample()
    {
        MyVisitorImplementation visitor = new MyVisitorImplementation();
        List<object> objects = GetList();
        foreach(IAcceptor item in objects)
            item.Accept(visitor);
    }
    
    
    public interface IVisitor
    {
        public void Visit(MyAcceptorImplementation item);
        public void Visit(AnotherAcceptorImplementation item);
    }
    
    public class MyAcceptorImplementation : IAcceptor
    { 
        //Some Code ...
        public void Accept(IVisitor visitor)
        {
            visitor.Visit(this);
        }
    }
    

    To complete the code here is Visitor to write to Console if it visits my or another implementation of an acceptor.

    public class MyVisitorImplementation : IVisitor
    {
            public void Visit(MyAcceptorImplementation item)
            {
                Console.WriteLine("Mine");
            }
            public void Visit(AnotherAcceptorImplementation item)
            {
                Console.WriteLine("Another");
            }
    }
    

    For more useful examples and better explanation have a look at Visitor Pattern and Iterator Pattern

    EDIT: Here an example using both, the visitor and Iterator. The iterator is just the logic how to move through your aggregate. It would make more sense with a hierarchical structure.

    public VisitorExample2()
    {
        MyVisitorImplementation visitor = new MyVisitorImplementation();
        List<object> myListToHide = GetList();
    
        //Here you hide that the aggregate is a List<object>
        ConcreteIterator i = new ConcreteIterator(myListToHide);
    
        IAcceptor item = i.First();
        while(item != null)
        {
           item.Accept(visitor);
           item = i.Next();
        }
        //... do something with the result
    }