4.8 Visitor

A search tree has the interesting property that it contains it's elements in a particular oder. String is Comparable ; therefore, adt.Test can use adt.tree.Set to read words and output them.

In general a Container can be asked to present each of it's constitutents exactly once to a Visitor . This is easily accomplished for an array, a list, a search tree, or a hash table. Only the search tree will present it's elements as a sorted sequence.

Two methods are needed. One is used to send the Visitor to the Container :


adt/Visitable.java
// adt/Visitable.java
package adt;
/** framework for visitor pattern: ability to be visited.
 */
public interface Visitable {
 /** receive a visitor, manage the visit.
     @return true if the visitor always replies true.
   */
 boolean visit (Visitor v);
}
 


The other method is for the Container to send it's inhabitants to the Visitor :


adt/Visitor.java
// adt/Visitor.java
package adt;
/** framework for visitor pattern: visiting object.
 */
public interface Visitor {
 /** visit an object.
     @return true to continue visiting.
   */
 boolean visit (Object x);
}
 


The methods are hard to distinguish.

One should really not use a single interface : A Container is Visitable if it can receive visitors. An arbitrary object is a Visitor if it can be presented with objects.

Visitor can be made more interesting if visit() uses different signatures to automatically differentiate between different inhabitants.

A search tree should be Visitable to benefit from the sorting. If a Visitor is implemented to show the inhabitants in a StringBuffer the tree can elegantly display itself:


adt/tree/Set.java
  /** receive a visitor.
   */
 public boolean visit (Visitor v) { return visit(0, v); }
 protected boolean visit (int at, Visitor v) {
   return sub[at] != null ? sub[at].visit(v) : true;
 }
 /** permit symbolic dump.
   */
 public String toString () {
   class Dumper implements Visitor {
     protected StringBuffer buf = new StringBuffer();
     public boolean visit (Object o) {
       buf.append('\n').append(o); return true;
     }
     public String toString () { return buf.toString(); }
   }
   Dumper d = new Dumper(); visit(d);
   return super.toString()+" count "+count()+d.toString();
 }
}
 


Set sends a Visitor to sub[0] .

If at sub[at] there is an Element it must accept the Visitor .

An Element sends the Visitor to sub[0] , presents it's own value, and sends the Visitor to sub[1] as needed — this is a recursive inorder traversal.

    /** receive a visitor.
      */
    public boolean visit (Visitor v) {
      return visit(0, v) && v.visit(info) && visit(1, v);
    }

visit() with two arguments is inherited from Set to Element .

toString() for an adt.tree.Set is implemented with a Dumper object that records each value in it's StringBuffer and eventually returns the buffer.

Dumper is an example for a local class — the fourth and last version of an inner class, which appears in a block just like an anonymous class. Hoiwever, a local class has a name and can therefore be used for several objects. In this case an anonymous class would have been sufficient. Both types can access the same things but a local class can implement more than one interface, etc.