The visitor pattern

The visitor pattern allows you to separate an algorithm via the visitor class from an object structure (aggregating elements) so that the new operations can be added without modifying the object structure.

It's a cleaner way by design for an old way of C++ friend class, which is allowed to access the private members of another class.

In simple words, you break the class into two classes, one only with elements/variables, and another only with the methods. In this way, you keep the one with variables the same while varying the methods, that is, algorithms in the other class. You define the methods grouped together in an interface called visitor so that you can have more than one implementation of this visitor interface, which means that you can have a completely different set of implementations while working on the same set of attributes / variables / elements.

Before we jump into a realistic example code, let's close the definition by the UML class diagram:

We will use the same example we used for the chain of responsibility pattern, that is, filling up WeatherStructure, which is our Object Structure for the visitor pattern. It contains four elements IWeatherElement mapping to the element of the visitor pattern as Map, Temperature, Description, and Thumbnail.

We will have two versions of concrete visitors (YahooWeatherBuilder, ForecastIOWeatherBuilder); we will also see two ways of visitor (AnotherWeatherManipulator) method's signature implementation. Ensure that you download and see the full version of the code for visitor pattern and others.

The interface for the Element is as follows:

    /// <summary> 
/// Interface for the Visitable class (The class to be visited
by the visitor class)
/// </summary>
public interface IWeatherElement
{
void ManipulateMe(IWeatherManipulator weatherManipulator);
}

The interface for the Visitor class is as follows:

    /// <summary> 
/// Interface for the Visitor object
/// </summary>
public interface IWeatherManipulator
{
void ManipulateElement(IWeatherElement weatherElement);
}

The object structure that aggregates the weather elements looks like the following:

    public class WeatherStructure 
{
private Temperature _temperature;
private Map _map;
private WeatherThumbnail _weatherThumbnail;
private WeatherDescription _weatherDescription;

private IWeatherManipulator _weatherBuilder;

public WeatherStructure(IWeatherManipulator weatherBuilder)
{
_weatherBuilder = weatherBuilder;

_temperature = new Temperature();
_map = new Map();
_weatherThumbnail = new WeatherThumbnail();
_weatherDescription = new WeatherDescription();
}...

Our test client code looks like the following:

    WeatherStructure weatherStructure = new WeatherStructure(new 
YahooWeatherBuilder());
weatherStructure.BuildWeatherStructure();

To use the other implementation of the visitor pattern, that is, to apply the different algorithm on the same set of elements, that is, the object structure, we just have to do the following:

    WeatherStructure weatherStructure = new WeatherStructure(new 
ForecastIOWeatherBuilder());
weatherStructure.BuildWeatherStructure();

Let's take a look at the simple code for BuildWeatherStructure():

    void BuildWeatherStructure() 
{
_temperature.ManipulateMe(_weatherBuilder);
_map.ManipulateMe(_weatherBuilder);
_weatherThumbnail.ManipulateMe(_weatherBuilder);
_weatherDescription.ManipulateMe(_weatherBuilder);
}

This line, _temperature.ManipulateMe(_weatherBuilder), is basically element.visit(visitor).

A while ago, I mentioned that we could have a slight variation in the visitor interface, and it would look like the following:

    interface IAnotherWeatherManipulator 
{
void ManipulateElement(Map map);
void ManipulateElement(Temperature temperature);
void ManipulateElement(WeatherDescription weatherDescription);
void ManipulateElement(WeatherThumbnail weatherThumbnail);
}

For the first version of the visitor interface, the implementation looks like the following:

    class WeatherManipulator : IWeatherManipulator 
{
public void ManipulateElement(IWeatherElement weatherElement)
{
if (weatherElement is Map)
BuildMap(weatherElement);
else if (weatherElement is Temperature)
BuildTemperature(weatherElement);
else if (weatherElement is WeatherDescription)
BuildWeatherDescription(weatherElement);
else if (weatherElement is WeatherThumbnail)
BuildWeatherThumbnail(weatherElement);
}

This means that we have to check the exact type of element in order to manipulate it effectively; on the other hand, the other variation of the visitor interface as a concrete type is passed into its argument, so the type checking inside the function is not required. Both are valid implementations of the visitor pattern.

If you still have any confusion about this example visitor pattern, take a look at the complete source code and have fun.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.105.2