In a search for flawless Visitor (design pattern) Part I
Control Structures and run-time type checking
One of the most frequent activities involved in Object Oriented Programming is an interaction with some kind of polymorphic stricture where object A can appear in different forms (objects B, C and D).
That is objects B, C, D inherit from object A.
So we have a variable of base type (A in our case) and we might anticipate the object assigned to this variable to be any one of derived types.
And it is not so bad if we only have to “ask” the object assigned to our variable for some info from it or ask it to do something for us – the info or a job to be done for us will vary according to “who our object is”. That is in which form object assigned to variable of base type would appear; So far so good. We ask – it does. No problem.
But what if we have to do something (send, wrap, paint – whatever) with our object and the operation is different for every subtype?
And this is where code becomes ugly...
- countless and sometimes nested “if” statements,
- rigid couples of enumeration types with “switch” statements,
- or trivial type checking like “Hey, A!” “Are you B?” “Are you C?” and so on.
Those things have very bad impact on extensibility and maintainability of code.
Moreover, the first one of them (“if” statements) can with time increase complexity of code up to the point of complete stupor in attempt to make any further changes as described by Leo Brodie in chapter 8 “Minimizing Control Structures” from his book named “Thinking Forth” (page 228)
The ways of avoiding them in Case 1 scenario are outlined in an excellent article
“Writing Better Code -- Keepin' it Extensible...” from Matthew Cochran.
Why Visitor Design Pattern?
The Visitor is a design pattern which can help us with dispatching subtypes of some polymorphic structure to corresponding action we wish to perform on them, avoiding control structures and type checking in a scenario of Case 2.
Skipping numerous UML diagrams and code examples from the Web we can characterize implementation by expressing things this way.
1. Make polymorphic structure of our interest (The “Host”) to expect visits from some guy – Visitor by declaring “Accept” virtual method with corresponding parameter type where to accept our guest (- the Visitor).
2. Make each subtype of the Host override “Accept” virtual method where the subtype would kindly offer itself for visits to Visitor.
3. Make a class for Visitor (which might evolve with time into another polymorphic structure where each Visitor’s subtype might represent different sets of corresponding operations to perform on Host subtypes).
4. Make a Visitor class to get acquainted with the Host structure by declaring a bunch of overloaded “Visit” methods – each for every subtype of the Host.
The “magic” of dispatching subtypes of Host structure to corresponding operations that Visitor offers happens when we call “Accept” method on Host base type variable passing Visitor as an argument.
Once Visitor gets inside “Accept" method, the Owner (Host subtype) of an “Accept” method “asks” the Visitor to visit him by calling its “Visit” method and passing itself to it as an argument.
Since Visitor has overloaded “Visit” methods for every subtype of a Host structure the correct match is guaranteed.
The Visitor has its flaws however.
The necessity of having “Visit” methods for every subtype of Host structure creates dependency between Visitor and a Host. The Host structure also depends upon Visitor’s base class.
Furthermore, every time you have a new set of operations to perform on Host’s subtypes you have to create a new subtype of a visitor and override some or all “Visit” methods of a base class.
Due to the flow 1 we can’t create generic Visitor to be used for other Host structures since every other Host has its own subtypes which types have to be "engraved" into overloaded “Visit” method signatures as parameter type.
Those flaws have following corresponding implications:
We can’t create reusable package with base classes for Host and its Visitor without introduction of high probability to violate OCP (Open-Closed Principle) in the future. Especially for volatile structures.
That is with every creation of a new Host subtype anywhere else you’ll have to go back to Visitor’s base class and change it by adding one more overloaded “Visit” method for new Host subtype.
The implication of the second one is pollution of our code with even more Visitors’ subclasses to maintain.
We have to create a new Visitor polymorphic structure for each different Host structure, again adding more classes to maintain to our code base.
So the questions arise from all of this.
Can we have something better?
Would it be kind of flawless Visitor or may be anything else?
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
© 2012 softomiser
More by this Author
Decision Table with bitwise operation in C# as a simple solution to avoid countless nested if(s) and rigid "switches".
Creating Data-driven fluent UI.
No comments yet.