Applied Design Patterns with Java
Behavioral :: Visitor (331) {C ch 26}
Collaborations
- A client that uses the Visitor pattern must create a ConcreteVisitor object and then traverse the object structure,
visiting each element with the Visitor.
- When an element is visited, it calls the Visitor operation that corresponds to
its class. The element supplies itself as an argument to this operation to let the Visitor access its state, if necessary.
The following interaction diagram illustrates the collaborations between an object structure, a Visitor, and
two elements:
Consequences
Some of the benefits and liabilities of the Visitor
pattern are:
- Visitor makes adding new operations easy.
Visitors make it easy to add operations that depend on the components of complex objects. Define
a new operation over an object structure simply by adding a new Visitor. By contrast, to spread functionality over many classes, then change
each class to define a new operation.
- A Visitor gathers related operations and separates unrelated ones. Related behavior isn't spread over the classes defining the object
structure; it's localized in a visitor. Unrelated sets of behavior are partitioned in their own Visitor
subclasses. That simplifies both the classes defining the elements and the algorithms defined in the Visitors.
Any algorithm-specific data structures can be hidden in the Visitor.
- Adding new ConcreteElement classes is hard. The Visitor pattern makes it hard to add new subclasses of Element. Each new
ConcreteElement gives rise to a new abstract operation on Visitor and a corresponding implementation in every ConcreteVisitor class.
Sometimes a default implementation can be provided in Visitor that can be inherited by most of the ConcreteVisitors, but this
is the exception rather than the rule. The key consideration in applying the Visitor pattern is if there
is an urgent need to change the algorithm applied over an object structure or the classes of objects that make
up the structure. The Visitor class hierarchy can be difficult to maintain when new ConcreteElement
classes are added frequently. In such cases, it's probably easier just to define operations on the classes that
make up the structure. If the Element class hierarchy is stable, there is a need to continually add operations
or change algorithms, then the Visitor pattern will help manage the changes.
- Visiting across class hierarchies. An Iterator
(257) can visit the objects in a structure as it
traverses them by calling their operations. But an Iterator can't work across object structures with different types of elements.
This implies that all elements the Iterator can visit have a common parent class. Visitor
does not have this restriction. It can visit objects that don't have a common parent class, and any type of object
can be added to a Visitor interface, without being related through inheritance.
- Accumulating state. Visitors can accumulate state as they visit each element in the object
structure. Without a Visitor, this state would be passed as extra arguments to the operations that perform
the traversal, or they might appear as global variables.
- Breaking encapsulation. Visitor's approach assumes
that the ConcreteElement interface is powerful enough to let Visitors do their job. As a result, the pattern often forces providing public operations
that access an element's internal state, which may compromise its encapsulation.
Catalog Behavioral Prev Next