SgDotNet
Singapore Professional .NET User Group -For Cool Developers

Visitor Pattern

rated by 0 users
This post has 1 Reply | 0 Followers

Top 100 Contributor
Posts 18
hendry Posted: 04-14-2008 11:56 AM

This is my first time posting a new discussion on the forum. So please bear with me for any mistakes. Today I am going to discuss a useful but rarely used( maybe because of its difficulties :-P) pattern, visitor pattern.

 

According to dofactory.com definition of the pattern,

 

“Visitor pattern Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.”

 

In a nutshell, the pattern allows you to add, remove or modify an operation without changing the structure of the classes that it operates on, that is the Element class. The purpose of the element class is only to accept visitors. Inside the implementation in the Accept(), it will call visitor’s class visit() method. This is where the real implementation lies. Therefore as you can see, there is no change in the Element class(es) if there is a change of implementation to the visitors’ classes. Below is the structure of the Visitor pattern:  

 

 

 

using System;

using System.Collections.Generic;

 

namespace Patterns.GOF.Visitor

{

    //test pattern

    static void Main(string[] args)

        {

            StudentCollection students = new StudentCollection();

            students.AddStudent(new Bachelor("Harry",3.0));

            students.AddStudent(new Bachelor("Mary",3.5));

            students.AddStudent(new Masters("Barry",2.1));

            students.AddStudent(new Masters("Tom",2.8));

            students.AddStudent(new Bachelor("Terry",3.4));

            students.AddStudent(new Masters("Jen",3.7));

            students.AddStudent(new Bachelor("Roy",3.1));

            Course c1 = new HistoryCourse();

            Course c2 = new GeographyCourse();

            students.Accept(c1);

            Console.WriteLine();

            students.Accept(c2);

            Console.WriteLine();

            c1.Count();

            Console.WriteLine();

            c2.Count();

            Console.ReadLine();

        }

 

 

    //Element

    public interface CollegeTitle

    {

        void Accept(ICourse course);

    }

    //Visitor

    public interface ICourse

    {

        void Visit(CollegeTitle people);

    }

 

    public abstract class Course : ICourse

    {

       

        protected int MCount { get; set; }

        protected int BCount { get; set; }

       

 

        public virtual void Visit(CollegeTitle people)

        {

            if (people is Bachelor)

            {

                BCount++;

            }

            if (people is Masters)

            {

                MCount++;

            }

        }

 

        protected abstract bool CheckQualified(Student people);

 

        public abstract void Count();

 

    }

 

    //Visitor Concrete method

    public class HistoryCourse : Course

    {

        //Implementation of the visit method. The whole execution of the pattern lies

        //in this method. Changing the implementation of this method will not affect

        // the element classes'.

        public override void Visit(CollegeTitle visitable)

        {

            Student student = visitable as Student;

 

            base.Visit(visitable);

            if(CheckQualified(student))

            Console.WriteLine("Student {0} from {1} has been qualified to enroll for history course"

                                    , student.Name

                                    , student.CourseName);

        }

        public override void Count()

        {

            Console.WriteLine("A total of {0} Bachelors student has tried to enroll for History Course", BCount);

            Console.WriteLine("A total of {0} Masters student has tried to enroll for History Course", MCount);

        }

 

 

        protected override bool CheckQualified(Student student)

        {

                return (((student.GPA > 3.0) &&

                            string.Compare(student.CourseName, "bachelor")>=0) ||

                                 (student.GPA > 3.2) &&

                                         string.Compare(student.CourseName, "masters")>=0);

          

        }

    }

    //Visitor concrete method

    class GeographyCourse : Course

    {

        //Implementation of the visit method. The whole execution of the pattern lies

        //in this method. Changing the implementation of this method will not affect

        // the element classes'.

        public override void Visit(CollegeTitle visitable)

        {

            Student student = visitable as Student;

            base.Visit(visitable);

            if (CheckQualified(student))

            Console.WriteLine("Student {0} from {1} has been qualified to enroll for geography course"

                                    , (student).Name

                                    , (student).CourseName);

        }

        public override void Count()

        {

            Console.WriteLine("A total of {0} Bachelors student has tried to enroll for Geography Course", BCount);

            Console.WriteLine("A total of {0} Masters student has tried to enroll for Geography Course", MCount);

        }

 

        protected override bool CheckQualified(Student student)

        {

                return (((student.GPA > 2.8) &&

                            string.Compare(student.CourseName, "bachelor")>=0) ||

                                 (student.GPA > 3.0) &&

                                         string.Compare(student.CourseName, "masters")>=0);

        }

    }

 

    public class Bachelor :Student

    {

        public Bachelor(string name,double gpa): base(name,gpa,"Bachelor")

        {

               

        }

    }

 

    public class Masters : Student

    {

        public Masters(string name,double gpa): base(name,gpa,"Masters")

        {

 

        }

    }

    //Element

    public class Student : CollegeTitle

    {

        public string Name { get; set; }

        public string CourseName { get; set; }

        public double GPA { get; set; }

 

        public Student(string name, double gpa, string courseName)

        {

            this.Name = name;

            this.CourseName = courseName;

            this.GPA = gpa;

        }

 

        public void Accept(ICourse course)

        {

            course.Visit(this);

        }

    }

    //Object structure

    class StudentCollection

    {

        List<Student> students = new List<Student>();

       

        public void AddStudent(Student student)

        {

            if (!students.Contains(student))

            {

                students.Add(student);

            }

        }

        public void RemoveStudent(Student student)

        {

            if(students.Contains(student))

            {

                students.Remove(student);

            }

        }

        //iteratively calling element accept method.

        public void Accept(ICourse course)

        {

            foreach (Student student in students)

            {

                student.Accept(course);

            }

        }

 

    }

   

}

 

 

As you can see, the most important method in the structure lies in the Implementation of the Course.Visit() method. The core execution is done by hosting the implementation on another class. Therefore, you can easily extend the capability of the element class by adding another concrete visitor(in this case is XXCourse)implementation which implement IVisitor(in this case is ICourse) interface.

Although this is a very useful method, there are guidelines to using this:

• You have a class hierarchy that is effectively sealed.

• There are many distinct operations to perform on it.

• The operations are orthogonal to the core purpose of the types in the hierarchy.

• You need the flexibility to define new operations over time.

I hope this post will give a better insight on visitor pattern. Do post your review / better alternatives on this pattern.

Top 25 Contributor
Posts 166

ah, the visitor pattern Smile nice insight on it here:

http://www.objectmentor.com/resources/articles/Craftsman51.pdf

http://devpinoy.org/blogs/cruizer

Page 1 of 1 (2 items) | RSS
Copyright SgDotNet 2004-2008
Powered by Community Server (Commercial Edition), by Telligent Systems