In preparing for the second section of inquiry for this project ie. Generics & Design Patterns I realized that my conception of the visitor design pattern did not match with the classical view. I set about to find what in the classical view I disagreed with and why. Since Design Patterns by Gamma et. al. is the definitive patterns book I looked to it as the classical view. I realized from this that what is actually written encompassed my view but was somewhat biased toward the more classical view. What follows is a section by section discussion of where I take issue with their discussion of the visitor design pattern.
I can find no fault with their stated intent other than that I might throw in the words variant and invariant because to me they are so central to what the visitor is about. It would then become, Represent a variant operation to be performed on the elements of an invariant object structure. Visitor lets you define new operations without changing the classes of the elements on which it operates.
The motivation section is also generally good. The example chosen is a place where I would likely use a visitor myself. The programming language certainly should be invariant within the project. However, between language versions it wouldn't be invariant so a better example could be found which would stay invariant even between program versions.
The third paragraph is the only one which I really think needs changed. It discusses the implementation problems that result from mixing the node hierarchy with the algorithms that operate on it. What is really the issue here is the separation of the invariant and fundamental parts form the variant parts. This is the reason for these problems and should be the real subject of the paragraph.
The third consequence is Adding new ConcreteElement classes is hard.
This should discuss the separation of invariant and variant code as this is not really a problem if the code is properly separated along those lines. People would be surprised what is invariant once they really understand the problem.
The sixth consequence is Breaking Encapsulation.
This states that it is often necessary to expose internal object state thereby breaking encapsulation. In my experience this simply isn't a problem. If one defines what is invariant and exposes that there isn't a problem.
The implementation section is weighted toward examples where the structure determines which nodes to visit. This should be down played because that is really much more like an iterator or a command which is mapped over the structure. Doing that assumes that the order of visiting nodes as well as which nodes to visit is invariant which is frequently not the case. This is discussed in the second implementation issue section. Which should be reworked to match the new emphasis.
This section seems to imply that any composite should simply send the visitor to all its children. I disagree, since the visitor should be the one traversing the structure in most cases, the composite must reveal itself as such and allow the visitor the choice of how to handle it.
This section should list the State Pattern as well since a visitor can be used to determine what state an object is in and to add variant operations that are dependent on state to the object. The one issue with this is that since the state can change there is the possibility that the state will change after the double-dispatch which determines that state. In this case the visitor would be working on an object of a different type
than expected (remember that state change is often called dynamic reclassification). This may necessitate some sort of locking mechanism for multi-threaded applications. This should be added to the examples as well.
In general the discussion is good except for the problems listed above. In addition I would extend the discussion by suggesting that the visitor can be used in more diverse situations to provide a hook on an abstract class hierarchy for variant behavior. Indeed it can be useful to provide an accept method for objects which are not even part of an abstract hierarchy to provide this hook. For example, consider a string class. If one attempted to provide all possible string operations that class would be far to big and constantly updated. By providing a visitor only the minimal set of operations would be on the string class and operations could be added dynamically through the visitor hook.