Visitor Pattern


The visitor pattern is a way of separating an operation from an object structure on which it operates. Its effect is to add new virtual functions to a family of classes without modifying the classes themselves.

In more every-day terms, the visitor pattern allows different types of visitor to visit different types of property and perform different kinds of operations. In our computer world we can’t have people do the visiting, nor answering the doors because ‘people’ implies intelligence and, as we all know, computers may be complicated, but they are not intelligent. So our properties are automated and our visitors are little robots.

There are several elements to this design:

1.    The types of property that can be visited, e.g. homes, businesses, lock-ups, phone-boxes. These are the visitable types.

2.    A factory producing programmable robots to visit the properties.

3.    A variety of customised visitor robots, e.g. postman, meter-reader, environmental health officer, policeman, bailiff. There is one visitor type for each operation that is to be performed.

In addition, this pattern is usually adopted where we want to visit a collection of properties in turn. So we also usually have:

4.    A means of associating the visitable properties – e.g. a village or town. (Usually implemented as a container of base-class pointers.)

5.    A mechanism for delivering the visitor to each visitable property – e.g. a taxi. (Usually implemented as an iterator.)

The main problem this pattern solves is how to apply a motley collection of actions on an existing stock of properties which don’t already have “buttons” for those actions built in. It also allows us to apply the same actions to a variety of different types of properties. If we were only dealing with a single type of property, then the simplest approach might be to follow the usual object-oriented practice of building each action into the property itself - for instance we would give all our houses gas-meter reading buttons and post boxes. But the problem the visitor pattern solves is where we want to keep adding actions. The solution means we don’t need to modify the properties when we add a new action.

Where we have a motley collection of unrelated property types and a motley collection of actions to be performed on them then the visitor pattern really comes into its own.

Here we have several unrelated property types and we don’t want to insist that lockups and phone-boxes have dummy post-boxes. Our stupid robots will blow a fuse if they can’t find the expected interface (a post-box) on each property which the taxi delivers them to. The taxi is also stupid. It doesn’t know which properties have whatever interface its particular robot requires.

We don’t want to pollute the front walls of our properties with a myriad of unused functions. But even more importantly, we don’t want to have to go round the entire town retro-fitting capability every time the authorities on high decide to introduce some new house-to-house inspection. If we build these operations into our properties, then every time a new operation is required, we have to make modifications to every property type. If we forget then our town-wide operation will return incomplete results, or may even terminate our attempt.

The aim of any design pattern is to isolate the places in a design that need to change when new requirements get added. Notice how the visitor pattern, as described above, achieves this by following the single responsibility principle (each part must have a single responsibility). One part defines visitable properties; one part defines the physical design of the robot-visitors. Another part programs the visitor robots to perform their tasks, another part collects the properties into an entity and the final part delivers the visitor to each property in turn. The visitor pattern’s particular strength is in isolating the thing that needs to change when a new operation is added. Instead of building these motley actions into the properties, we build them into the robot visitors. The job of the taxi-driver (the iterator) is to plug the robot into a standard robot-portal on each property in the town, and when the robot has completed its task take it to the next property and do the same. All we ask of the property is to press the “go” button on the look-alike robot when it gets plugged in. All robots look the same, so neither the taxi driver nor the properties have any difficulty knowing what to do.

The clever bit is designing the robot. The robot has to know how to perform its designated operation on each type of property. The person who programmes the robot has to know what functions are available in each type of property to enable it to complete its task. Here we are talking about a real live person, sitting at a keyboard applying genuine intelligence. Since the available functions are likely to be quite different for each property the robot has to have some way of knowing which type of property has pressed its “go” button. We could make each type of property press a different “go” button, but how many “go” buttons do we want? If the factory makes 5-button robots and we end up adding a sixth type of property, then all our old robots will have to be thrown away and a new design produced. The solution to this little conundrum is to embed a finger-print reader into the surface of the “go” button. Then we give each type of property a unique finger-print on the automated arm which presses the “go” button.

We have now completed the implementation of the visitor pattern. All visitable properties are fitted with a city-wide standard “go-button-presser” stamped with a thumb-print indicating to the visitor-robot its property type. Anyone who wants to implement a city-wide visitation for some new task buys a standard robot and using a list of existing property types programmes it to perform that one task for each property type. Adding a new operation has been localised in the hidden inner workings of the standard robot.

Adding a new property type is slightly more problematic, and this is a weakness of the Visitor Pattern. Any time a new property type is introduced to the city, every robot will have to be re-programmed to tell it how to achieve its purpose on the new property type. The good thing is that none of the existing programming will need to be touched.

Now you have a clear grasp of what the visitor pattern is for and the strategy for achieving it, we turn our attention to its implementation in C++.

Once you understand the concept, the implementation is reasonably trivial. Following good design practice, we start with the easiest and most stable bits.

The City

The city is implemented as a container of some sort. The choice of container depends upon the volatility of the city. If it is stable then a vector will probably be fine. If buildings are going up all over the place then a deque would be more efficient. If we wanted to be a bit more sophisticated and be able to choose what type of property our taxi-driver visits, then a map of vectors or a map of deques could be made to sort our properties into sub-collections of different types. The map key would be obtained from the type_info for each property added, and the property pointer would be the associated value.

The Taxi

The taxi comes next. It is the iterator provided by the container we have chosen. Its job is to move from property to property and on each one call the accept() function.

The Property

Every property is required to implement an identical function, accept(). It takes as its single argument a visitor reference (or pointer). Its implementation calls the visit() function on the visitor. To enforce this we create an interface class for visitable properties to inherit from. It is IVisitable pointers that we will hold in our container.

The visitor pattern claims to “add new virtual functions to a family of classes without modifying the classes themselves.” Yet we have said that all visitable properties need to implement the accept() function. What if we do not own the code for our properties and they don’t have an accept function? The solution is to embed the property objects inside our Visitable classes. The preferred method is composition using delegating functions to republicise the property interface. If upcasting to the property type is required, multiple inheritance is the solution. Now we have families of property objects sharing a common root class and all implementing accept().

The finger-print must be given to the visitor robot, which is the “this” pointer for the property being visited. So visit() takes the property’s this-pointer as its sole argument.

 

class IVisitable {

public:

...

virtual void accept(IVisitor & myVisitor) = 0;

virtual ~IVisitable() {}

};

 

class somePropertyClass : public IVisitable {

public:

...

void accept(IVisitor & myVisitor) {

          myVisitor.visit(this);

}

};

The Visitor Robot Factory

An interface class defines the standard visitor public interface, building in the finger-print reader:

class IVisitor {

public:

  virtual void visit(House * visitable) = 0;

  virtual void visit(Business * visitable) = 0;

  virtual void visit(Lockup * visitable) = 0;

  ... add new overloaded visit for each property type

  virtual ~IVisitor() {}

};

The Programmed Visitor Robot

Each concrete visitor class is coded to call the appropriate methods available in each property type to complete its task.

class Visitor : public IVisitor {

public:

  void visit(House * visitable) {...}

  void visit(Business * visitable) {...}

  void visit(Lockup * visitable) {...}

  ... add new overloaded visit for each property type

};

Double-Dispatch

Our container iterator returns a base-class property pointer (IVisitable*). Polymorphism ensures that when we use that to call the accept function, the inherited class accept function is called, for instance the Lock_Up.accept(). This is called a “single-dispatch”; one polymorphic function call.

However something subtle takes place next. Lock_Up.accept() is passed an IVisitor reference (or pointer) which it uses to call the visit function. Polymorphism comes into play a second time to select the particular visitors visit(), for instance the Meter_Reader visitor. This is our second dispatch. One final selection takes place; this time using the this pointer from the visited property to determine which overloaded function gets called. Polymorphism plays no part here, this is simple type-matching performed by the compiler (called static-dispatch). The result is that our properties behave as if we have added new virtual functions, one for each visitor type.

Duplicate accept() functions

All the accept() functions are the same, so you might think we should make it a base class function. If you did this, the compiler would require the Visitors to provide visit() functions that could take a base-class visitable pointer (IVisitable*) and that visitor function would always get chosen by the compiler instead of the overloaded visitor functions taking inherited visitable pointers.

class Visitor : public IVisitor {

public:

  void visit(IVisitable * visitable) {;} // best match empty

  void visit(House * visitable) {...}

  void visit(Business * visitable) {...}

  void visit(Lockup * visitable) {...}

  ... add new overloaded visit for each property type

};

 

 

A template could be used to generate visitable classes with the accept() function.