ALLINSIGHT

Home of the AlmostImplementedException

Visitor Pattern

As announced in my post about the “Null Object Pattern” I will explain the “Visitor Pattern” today. At the first time I maintained an application with visitors, I found it a bit confusing – Therefore I try to explain it based on my own experiences. The Visitor is a pattern of the category of behavior patterns which serves to seperate algorithms/logic from the object structure.

Visitor (UML)

That means you define the processing of the object outside of it. In the UML diagramm we define a visitor interface (IVisitor) and an element interface (IElement). The conrete elements (ElementA, ElementB) implement that interface and the “Accept” method, that is essentially for the visitor behaviour.
The content of the concrete “Accept” method is simple. It gets an object that implements the “IVisitor” interface (VisitorX) and calls the “Visit” method of the visitor. So “ElementA” will call “Visit(ElementA element)”, “ElementB” will call “Visit(ElementB element)”, and so on. The visitor will call the “Accept” method of the object that implements “IElement” and is able to process the concrete implementation (ElementA or ElementB) in the defined method e.g. “Visit(ElementA element)”. This behaviour will allow you to make structured, type based processing of objects. If you want to have more Visitors you can simply create a new class that implements the IVisitor interface (e.g. VisitorY) without changing anything in the object structure (Elements).
At next we want to implement a visitor in our chess board example – but first I will show you a more practical example.

Visitor Example (UML)

You can see, that we have two conrete objects called “UserObject” and “CommonObject” that implements the “IDbObject” interface. In our class “DbSaver” we need to handle the objects different. Therefore we implement the “IDbObjectVisitor” that will take over the seperation of the concrete “DbObject” type and we will implement the concrete save logic in the “Handle” Method for concrete “IDbObjects”. To make it more clear, take a look at the following example source code. Just create an instance of the “DbObjectController” and call “Execute” 😉

Please remember at this point, to the null object pattern. You should implement a class for an empty object instead of using “null” to prevent “NullReferenceException” in the “DbObjectSaver”.
You can also define the visitor interface with return values (generics are also possible, I will show this in the next example for our chess board).
In our chess board we have different figures, so it makes sense, that we implement the visitor for these figures. So let’s create an interface for the normal behaviour (void as return) and an interace with generic return value behaviour (I will show the usage later).

Next we have to add the abstract “Accept” methods in the “Figure” class and make the concrete implementations in the conrete “Figure” classes. Because that’s always the same for each figure, I will only show the example for the class “King”.

Now we are able to use the visitor. Let’s say we want to implement a “MoveFigure” functionality. We want a notification when a figure is killed by another, with the following output: Field, Figure and Color. For example “A8:Rook (Black) is killed by Rook (White)”. To get this behavior, we must implement the “IFigureVisitor” in the “Field” class. We use the generic visitor with string als return value to get the following output from the “Handle” (for example for the Rook) method: “Rook (White)”. For the “Empty” figure we will return an empty string. At next we want to generate the concrete output, when we assign a “Figure” to a “Field”, except when we assign “Empty” or if “Empty” is the current value. The new “Field” class will looks like the following:

Finally lets add the “MoveFigure” method to the “Board” class and use it 🙂 The Result will be: “A5:Pawn (Black) is killed by Pawn (White)”

That’s all! You maybe have noticed that the implementation for the visitor pattern is coupled at some effort. So use it wisely. The other point is that you should use it only for closed systems and not for object structures that offer inheritance (outside his scope), because it is necessary that you adapt the visitor interface. Let’s say we have our “DbObject” example in a external library and the user wants to add a new “DbObject” like (e.g. GroupObject) that implements the “IDbOjects” interface. He will not be able to add the visitor behaviour because the necessary interface is in the external library. Also he can not implement a new Visitor interface because for this he must add a new “Accept” method in the existing “DbObjects”. That is the main reason why the visitor pattern is not used as often as other patterns – the extensibility. So take your time to find out the best use case for you 😉

Share :

, , , , , , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *