Click here to Skip to main content
Click here to Skip to main content

Visitor Design Pattern

By , 10 Sep 2009
 

Background

Again, my favorite place - Elizabeth's daycare center.

From my previous article, I talked about how to use the Strategy Pattern to develop a system for the doctor visiting. In that case, I only focused on the Doctor objects to illustrate how those dynamic actions will be impacted when the strategy object (different doctors) changes.

In this article, I am going to focus on those visitable/Kid objects which are examined by the Doctors.

On Elizabeth's daycare calendar, it shows Dr. WANG (EyeDoctor) gives eye screens on the 15th each month for all the kids, Dr. Fong (SLP - Speech Language Pathologist) gives speech evaluations on the 28th each month. And parents may visit their kids at anytime.

For each visitor's visit, all the related status needs to be updated after the Doctor's visits. EyeDoctor will update EyeStatus only, and SLP updates SpeechStatus as well. Parents will update both EyeStatus and SpeechStatus.

Remember, Kids could be visited by different visitors. It could allow any types of visitors to visit, however, the visitors should know what status needs to be updated for the kid after their visits.

Introduction

The Visitor Pattern could help us to decouple the operation details for a particular visitable object from multiple visitor objects. We isolate the responsibilities in the separate visitor objects(such as EyeDoctor, SLP & Parent) when those objects need to implement their own logic for the visitableObject(Kid).

This article introduces an implementation of how we use the visitor pattern to our daycare center.

Visitor Design Pattern Structure

visitorS.JPG

Class Diagram

VisitorClass.JPG - Click to enlarge image

Implementation Code

Visitor Objects

Visitor

Visitor class is our base abstract class. One abstract public method (Visit) has been declared in here to allow the child class to implement the details. It takes a VisitableObject object as the parameter. It actually provides the ability to the child Visitor class to work around it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace VisitorDesignPattern
    {
        public abstract class Visitor
        {
            abstract public void Visit(VisitableObject akid);
        }
    }
}

EyeDoctor

In my EyeDoctor class, a child Visitor class, it will only update the eyestatus for an incoming visitableObject.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace VisitorDesignPattern
    {
        public class EyeDoctor : Visitor
        {
            //Visit the visitableObject and update the deserved information
            public override void Visit(VisitableObject akid)
            {
                Kid kid = (Kid)akid;
                kid.EyeStatus = "Status updated from EyeDoctor - 
		Message from Dr.WANG, Eye Infections found for " + kid.Name + ". 
		Please schedule an appointment asap";
            }
        }
    }
}

SLP

Same as EyeDoctor, SLP class will only update the Speechstatus for an incoming visitableObject.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace VisitorDesignPattern
    {
        public class SLP : Visitor
        {

            public override void Visit(VisitableObject akid)
            {
                Kid kid = (Kid)akid;
                kid.SpeechStatus = "Status updated from SLP - This is Dr.FONG, " + 
			kid.Name + " did a good job on the speech exam.";
            }
        }
    }
}

Parent

Same as EyeDoctor, Parent class will update both Eyestatus and Speechstatus for an incoming visitableObject.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace VisitorDesignPattern
    {
        public class Parent : Visitor
        {
            public override void Visit(VisitableObject akid)
            {
                Kid kid = (Kid)akid;
                kid.EyeStatus += Environment.NewLine + 
			"Status updated from Parent -- This is " + kid.Name + 
			"'s parent, I updated the EyeStatus.";
                kid.SpeechStatus += Environment.NewLine + 
			"Status updated from Parent -- This is " + kid.Name + 
			"'s parent, I updated the SpeechStatus.";
            }
        }
    }
}

VisitableObject Class

VisitableObject

VisitableObject class is an abstract base class which has one public method(AcceptVisitor). AcceptVisitor method is the key method in our visitor pattern. It takes one Visitor object as the parameter, which will call its Visit function to visit visitableObject itself (an instance of visitableObject) in order to achieve the visit pattern.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace VisitorDesignPattern
    {
        public abstract class VisitableObject
        {
            public void AcceptVisitor(Visitor visitor)
            {
                //allow the incoming visitor to start visiting
                visitor.Visit(this);
            }
        }
    }
}

Kid Class

Kid class in here is a helper class that inherits from the VisitableObject. It holds personal information for each child.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace VisitorDesignPattern
    {
        public class Kid : VisitableObject
        {
            public string Name { get; set; }
            public int Age { get; set; }

            public string EyeStatus { get; set; }
            public string SpeechStatus { get; set; }
        }
    }
}

Client App

From the client side, I create two kids as our visitableObject objects, and add them to the daycare. And I also create a visitor collection, it contains an EyeDoctor, a SLP and a Parent. For each kid, she/he will be visited by each visitor from the visitor collection.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using www.askbargains.com.VisitorDesignPattern;

namespace www.askbargains.com
{
    namespace Client
    {
        class Program
        {
            static void Main(string[] args)
            {
                List<kid> dayCare = new List<kid>();

                //Kid Elizabeth is created
                Kid Elizabeth = new Kid();
                Elizabeth.Name = "Elizabeth";
                Elizabeth.Age = 3;
                //add Elizabeth to daycare
                dayCare.Add(Elizabeth);

                //Kid Aimee is created
                Kid Aimee = new Kid();
                Aimee.Name = "Aimee";
                Aimee.Age = 4;
                //add Aimee to daycare
                dayCare.Add(Aimee);

                //create Visitors
                List<visitor> visitors = new List<visitor>();
                visitors.Add(new EyeDoctor());
                visitors.Add(new SLP());
                visitors.Add(new Parent());

                //start looping all the visitors
                foreach (Visitor visitor in visitors)
                {
                    //kid start to be visited for each visitor
                    foreach (Kid oneKid in dayCare)
                    {
                        oneKid.AcceptVisitor(visitor);
                    }
                }

                //display result
                foreach (Kid oneKid in dayCare)
                {
                    Console.WriteLine("Display Status for " + oneKid.Name);
                    Console.WriteLine(oneKid.EyeStatus);
                    Console.WriteLine(oneKid.SpeechStatus);
                    Console.ReadLine();
                }
                Console.ReadLine();
            }
        }
    }
}

Once we start our client app, you will see all that the correspond status has been updated after each visitor's visiting in the output window. Cool!

VisitorOutput.JPG

Conclusion

From this article, I demonstrated how we can use the Visitor Pattern to achieve the implementation for a Daycare visitor. I also use the daycare center for the Decorator Design pattern in my other article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Allen _ Wang
Architect
United States United States
Member
Sr Software Architect.Microsoft Certified Solutions Developer (MCSD). owner of www.askbargains.com.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralAnd yet...memberVitaly Tomilov23 Jun '12 - 13:08 
...medical system in the USA remains to be one of the worst Shucks | :->
 
Good article, by the way Wink | ;)
GeneralMy vote of 5memberAkram El Assas10 May '12 - 3:10 
Good article. Here[^] is a functional implementation sample.
QuestionWhat if...memberrichard.guzik15 Sep '09 - 4:11 
... you add a new VisitableObject type (e.g. Adult)?
 
Actually, in the Visitor pattern, the visiting methods should be dedicated to each type of visited object. You may also provide a generic method (as a default), but in that case it does not make sense to downcast the VisitableObject.
AnswerRe: What if...memberSmartAllen15 Sep '09 - 5:42 
Agree with you.
But remember, the visitor pattern will focus on the same object to be visited by different visitors, not same visitor to visit different visitable objects.
However, in order to handle the case you gave (new visitableObject Adult), the Visit() method in the Visitor class needs to take care the job to distinguish what type of VisitableObject it will work with and give the specific implementation for it.
 
something needs to be like:
 
public override void Visit(VisitableObject obj)
{
if(obj is Kid)
{
Kid kid = (Kid)obj;
//do visit kid work...
}
 
if(obj is Adult)
{
Adult kid = (Adult)obj;
//do visit adult work...
}
}
GeneralRe: What if...memberKeithAMS15 Sep '09 - 6:38 
I think this is missing the point of the visitor pattern.
 
rather than your
if (obj is Kid)
...
if (obj is Adult)
 
you should rather have
 
virtual void VisitKid(Kid kid)
virtual void VisitAdult(Adult adult)
 
in your base Visitor class
 

 
Your Concrete classes (Kid , Adult)
 
then have
class VisitableObject
{
abstract void AcceptVisitor(Visitor visitor)
}
 
class Kid
{
virtual void AcceptVisitor(Visitor visitor)
{
vistor.VisitKid();
}
}
 
class Adult
{
virtual void AcceptVisitor(Visitor visitor)
{
vistor.VisitAdult();
}
}
 

HTH anyway
 
KM
 
I'm pink therefore I'm Spam

GeneralRe: What if...memberstevenlauwers2215 Sep '09 - 7:01 
This would be a terrible violation against the open closed principle.
But, as you mentioned before, the Visitor Pattern wasn't designed to handle things like this.
 
Good article on the Visitor Pattern though Smile | :) .
GeneralRe: What if...memberKeithAMS15 Sep '09 - 21:50 
Thats how the pattern is defined in the "bible" 'Design Patterns' by the gang of four...
 
KM
 
I'm pink therefore I'm Spam

GeneralRe: What if...memberrichard.guzik15 Sep '09 - 7:14 
Sorry, it was only to pinpoint the fact that, in the signature of Visit, you consider your visited object from an abstract point of view (VisitableObject), but you downcast it to its real type (Kid) in your implementations, with the risk of either doing a wrong cast (1st version) or forgetting to handle some subtypes (2nd version).
 
You must, at some time, take into account the real type of the object, but you do it unsafely by downcasting the VisitableObject.
 
That is why, in the visitor pattern,
(i) the type of the visited object appears explicitly in the signature (or even the name) of the method dedicated to the concrete visited class in the Visitor interface;
(ii) Accept() is implemented in each concrete class (not in VisitableObject) and calls the dedicated method by passing the receiving object itself.
GeneralRe: What if...memberSmartAllen15 Sep '09 - 7:43 
Good point. IVisitable it is... Wink | ;)
Generalnice chapter following the previous onememberuwspstar200910 Sep '09 - 3:56 
please keep posting the new chapters, nice article !!!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 10 Sep 2009
Article Copyright 2009 by Allen _ Wang
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid