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
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
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:
/** 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.