One of the many cool things we learned last week was on how to traverse a collection using a visitor.

”…the visitor design pattern is a way of separating an algorithm from an object structure. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.” - wikipedia

Let’s pretend that I’ve got an exam, and this exam has a bunch of questions and I want to find out how many questions have been completed. I could throw a method on my IExam type that returns the number of completed questions. Kind of like…

public class BadExam : IExam 
    {
        public BadExam( ) : this( new List> IQuestion >( ) ) {}

        public BadExam( IList> IQuestion > questions ) 
        {
            _questions = questions;
        }

        public int GetCompletedQuestions( ) 
        {
            int completedQuestions = 0;
            foreach ( IQuestion question in _questions ) 
            {
                completedQuestions += question.IsComplete( ) ? 1 : 0;
            }
            return completedQuestions;
        }

        private IList> IQuestion > _questions;
    }

But what happens when I want to find out other statistics about the questions in my exam. I could add additional methods to the IExam type for each type of information that I want to query on, but this would totally violate the Open/Closed Principle. Let’s try to solve the same problem above using a visitor…

public class Exam : IExam 
    {
        public Exam( ) : this( new List> IQuestion >( ) ) {}

        public Exam( IList> IQuestion > questions ) 
        {
            _questions = questions;
        }

        public void TraverseUsing( IVisitor> IQuestion > visitor ) 
        {
            foreach ( IQuestion question in _questions ) 
            {
                visitor.Visit( question );
            }
        }

        private IList> IQuestion > _questions;
    }

What this allows me to do is create new implementations of IVisitor’s that traverse the collection of questions but collect information that it needs. For example we could have a “CompletedQuestionsVisitor” that keeps track of the number of completed questions.

public class CompletedQuestionsVisitor : ICompletedQuestionsVisitor 
    {
        private int _completedQuestions;

        public void Visit( IQuestion question ) 
        {
            _completedQuestions += ( question.IsComplete( ) ? 1 : 0 );
        }

        public int TotalCompletedQuestions( ) 
        {
            return _completedQuestions;
        }
    }

Now I can traverse the internal collection of questions and pick out the information that I need. What if I wanted to build a tree of specifications and wanted to traverse a collection of items and keep track of only those items that match my composite specification. Introducing the “Specification Visitor”.

public class SpecificationVisitor> T > : ISpecificationVisitor> T > 
    {
        private readonly ISpecification> T > _specification;
        private int _totalMatching;

        public SpecificationVisitor( ISpecification> T > specification ) 
        {
            _specification = specification;
        }

        public void Visit( T item ) 
        {
            _totalMatching += _specification.IsSatisfiedBy( item ) ? 1 : 0;
        }

        public int TotalMatching( ) 
        {
            return _totalMatching;
        }
    }

Yes it’s a little overboard and slightly contrived, but perhaps someone can find a good home for him. Source Code (2.28MB)

comments powered by Disqus