BlackWaspTM
Design Patterns
.NET 2.0+

Visitor Design Pattern (2)

The visitor pattern is a design pattern that separates a set of structured data from the functionality that may be performed upon it. This promotes loose coupling and enables additional operations to be added without modifying the data classes.

Implementing the Visitor Pattern

Visitor Design Pattern UML

The UML class diagram above shows an implementation of the visitor design pattern. The items in the diagram are described below:

  • Client. The Client class is a consumer of the classes of the visitor design pattern. It has access to the data structure objects and can instruct them to accept a Visitor to perform the appropriate processing.
  • ObjectStructure. The ObjectStructure class holds all of the elements of the data structure that can be used by visitors. The elements may be held in a simple collection or a more complex structure. The class includes a method, in the above diagram named "Accept", that can be called by the client with a Visitor object passed as a parameter. The ObjectStructure class then enumerates the contained elements, calling the Accept method of each and passing the provided Visitor. This allows each element to be processed without the client requiring any knowledge of the elements beforehand.
  • ElementBase. This abstract class is the base class for all element objects. It defines the accept method that each element must implement in order to be visited.
  • ConcreteElement A/B. Concrete element objects are those that hold real information in the data structure. To enable their use with the visitor design pattern, each must implement the Accept method. Usually the Accept method simply performs a callback to the visitor object. When the callback is made, the element object is passed to the visitor's Visit method so that it may execute an algorithm using the element's data.
  • VisitorBase. This class is the abstract base class for all concrete visitors. It defines a method, in the above diagram named "Visit", that can be called by element objects during the callback process. The method is generally overloaded with a version that is capable of processing any of the concrete element types.
  • ConcreteVisitor A/B. The concrete visitor classes contain the operations that are applied to the concrete element objects. They implement the various overloaded Visit methods defined in the VisitorBase class.

The following shows the basic code of the visitor design pattern implemented using C#. To simplify the example the code uses C# 3.0 automatically implemented property syntax to define properties. For earlier versions of the language you should use full property declarations with backing variables.

public class ObjectStructure
{
    public List<ElementBase> Elements { get; private set; }

    public ObjectStructure()
    {
        Elements = new List<ElementBase>();
    }

    public void Accept(VisitorBase visitor)
    {
        foreach (ElementBase element in Elements)
        {
            element.Accept(visitor);
        }
    }
}


public abstract class ElementBase
{
    public abstract void Accept(VisitorBase visitor);
}


public class ConcreteElementA : ElementBase
{
    public override void Accept(VisitorBase visitor)
    {
        visitor.Visit(this);
    }

    public string Name { get; set; }
}


public class ConcreteElementB : ElementBase
{
    public override void Accept(VisitorBase visitor)
    {
        visitor.Visit(this);
    }

    public string Title { get; set; }
}


public abstract class VisitorBase
{
    public abstract void Visit(ConcreteElementA element);

    public abstract void Visit(ConcreteElementB element);
}


public class ConcreteVisitorA : VisitorBase
{
    public override void Visit(ConcreteElementA element)
    {
        Console.WriteLine("VisitorA visited ElementA : {0}", element.Name);
    }

    public override void Visit(ConcreteElementB element)
    {
        Console.WriteLine("VisitorA visited ElementB : {0}", element.Title);
    }
}


public class ConcreteVisitorB : VisitorBase
{
    public override void Visit(ConcreteElementA element)
    {
        Console.WriteLine("VisitorB visited ElementA : {0}", element.Name);
    }

    public override void Visit(ConcreteElementB element)
    {
        Console.WriteLine("VisitorB visited ElementB : {0}", element.Title);
    }
}
21 August 2009