//
Purpose. Visitor design pattern
//
1. Add an accept(Visitor) method to the "element" hierarchy
//
2. Create a "visitor" base class w/ a visit() method for every
"element" type
// 3. Create a "visitor" derived class
for each "operation" to do on "elements"
// 4. Client
creates "visitor" objects and passes each to accept() calls
interface
Element {
public void accept(
Visitor v ); // first dispatch // 1.
accept(Visitor)
} // interface
class This implements
Element {
public void accept( Visitor v ) { v.visit( this ); } //
1. accept(Visitor)
public String
thiss() { return
"This"; } // implementation
}
class
That implements Element {
public
void accept( Visitor v ) { v.visit(
this ); }
public String
that() { return
"That"; }
}
class TheOther implements Element {
public void accept( Visitor v ) { v.visit( this ); }
public String theOther() { return "TheOther";
}
}
interface Visitor { // 2. Create a
"visitor"
public void
visit( This e ); ////// second dispatch //
base class with a
public
void visit( That e );
// visit() method for
public void visit( TheOther e ); // every "element"
}
// type
class
UpVisitor implements Visitor { // 3. Create a "visitor"
public void visit( This e ) { // derived class for
System.out.println( "do Up on
" + e.thiss() ); } // each
"operation"
public
void visit( That e ) {
// to perform on
System.out.println( "do Up on
" + e.that() ); } // "elements"
public void visit( TheOther e ) {
System.out.println( "do Up on
" + e.theOther() ); }
}
class DownVisitor implements
Visitor {
public void visit(
This e ) {
System.out.println( "do Down on " + e.thiss() ); }
public void visit( That e ) {
System.out.println( "do Down on
" + e.that() ); }
public
void visit( TheOther e ) {
System.out.println( "do Down on " + e.theOther() ); }
}
class
VisitorDemo {
public static
Element[] list = { new This(), new That(), new TheOther() };
public static void main( String[] args )
{
UpVisitor up
= new UpVisitor(); //
4. Client creates
DownVisitor down = new
DownVisitor(); // "visitor" objects
for (int i=0; i < list.length;
i++) // and passes each
list[i].accept( up ); // to accept() calls
for (int i=0; i < list.length;
i++)
list[i].accept( down
);
} }
// do Up on
This // do Down on
This
// do Up on That
// do Down on That
// do Up on TheOther // do Down on TheOther
//===============================================================================
//
Purpose. Double dispatch (within a
single hierarchy)
//
// Discussion.
We would like to declare a function like:
// void process( virtual Base object1,
virtual Base object2 )
// that does the right thing based on the type of 2
objects that come from
// a single inheritance hierarchy. The only problem is that the keyword
//
"virtual" may not be used to request dynamic binding for an object
being
// passed as an argument.
Java will only "discriminate" the type of an object
//
being messaged, not the type of an object being passed. So in order for
// the type of 2
objects to be discriminated, each object must be the
// receiver of a
virtual function call. Here, when
process1() is called on
// the first object, its type becomes
"known" at runtime, but the type of
// the second is still
UNknown. process2() is then called on
the second
// object, and the identity (and type) of the first object is
passed as an
// argument. Flow of
control has now been vectored to the spot where the
// type (and identity)
of both objects are known.
public class VisitorSingle {
interface
Base {
void process1( Base secondObject
);
void process2( A
firstObject );
void process2( B firstObject );
void process2( C firstObject );
}
static class A implements Base {
public void process1( Base second ) {
second.process2( this ); }
public void process2( A first ) {
System.out.println( "first is A, second is A" );
}
public void process2( B first
) {
System.out.println(
"first is B, second is A" ); }
public void process2( C first ) {
System.out.println( "first is C,
second is A" ); }
}
static class B implements Base {
public void process1( Base second ) {
second.process2( this ); }
public void process2( A first ) {
System.out.println( "first is A, second is B" );
}
public void process2( B first
) {
System.out.println(
"first is B, second is B" ); }
public void process2( C first ) {
System.out.println( "first is C,
second is B" ); }
}
static class C implements Base {
public void process1( Base second ) {
second.process2( this ); }
public void process2( A first ) {
System.out.println( "first is A, second is C" );
}
public void process2( B first
) {
System.out.println(
"first is B, second is C" ); }
public void process2( C first ) {
System.out.println( "first is C,
second is C" ); }
}
public static void main( String[] args
) {
Base array[] = { new A(),
new B(), new C() };
for (int
i=0; i < array.length; i++)
for (int j=0; j < 3; j++)
array[i].process1( array[j] );
}}
// first
is A, second is A
// first is A, second is B
// first is A, second is
C
// first is B, second is A
// first is B, second is B
// first
is B, second is C
// first is C, second is A
// first is C, second is
B
// first is C, second is C
//===============================================================================
//
Purpose. VisitorComposite1 is a basic
Composite implementation with one
// recursive traversal method. VisitorComposite2 is a non-Visitor
implementation
// that models "parsing" the hierarchical
Composite with the collect() recursive
// traversal method. VisitorComposite3 is a Visitor
implementation.
//
// Highlights.
VisitorComposite2 changes interface Component into an abstract
//
class. It requires protected static
members. VisitorComposite3 is
"open for
// extension, but closed for modification". The interface Component remains an
//
interface. Now that "collect"
is an object, many of them can be created and
// can operate
simultaneously (the previous static attributes would have required
//
significant extra effort to provide this functionality). Drawback: the public
// interface of
Leaf and Composite had to be extended.
import java.util.*;
interface
Component { void traverse(); }
class Leaf implements Component
{
private int number;
public Leaf( int num ) { number = num;
}
public void traverse() {
System.out.print( number + " " ); }
}
class Composite
implements Component {
private
static char next = 'a';
private
List children = new ArrayList();
private char letter = next++;
public void add( Component c ) { children.add( c ); }
public void traverse() {
System.out.print( letter + " "
);
for (int i=0; i <
children.size(); i++)
((Component)children.get(i)).traverse();
} }
public
class VisitorComposite1 {
public
static void main( String[] args ) {
Composite[] containers = new Composite[3];
for (int i=0; i < containers.length;
i++) {
containers[i] = new
Composite();
for (int j=1; j
< 4; j++)
containers[i].add( new Leaf( i * containers.length + j ) );
}
for (int i=1; i < containers.length; i++)
containers[0].add( containers[i]
);
containers[0].traverse();
System.out.println();
} }
// a 1 2 3 b 4 5 6 c 7 8
9
//------------------------------ VisitorComposite2
------------------------------
import java.util.*;
abstract
class Component {
protected
static StringBuffer letters = new StringBuffer();
protected static StringBuffer numbers = new
StringBuffer();
public
abstract void traverse();
public
abstract void collect();
public
static String getLetters() { return letters.toString(); }
public static String getNumbers() { return
numbers.toString(); }
}
class Leaf extends Component {
private int number;
public Leaf( int num ) { number = num;
}
public void traverse() {
System.out.print( number + " " ); }
public void collect() {
numbers.append( number ); }
}
class Composite extends Component
{
private static char next =
'a';
private List children = new
ArrayList();
private char letter
= next++;
public void add(
Component c ) { children.add( c ); }
public void traverse() {
System.out.print( letter + " " );
for (int i=0; i < children.size();
i++)
((Component)children.get(i)).traverse();
}
public void
collect() {
letters.append(
letter );
for (int i=0; i <
children.size(); i++)
((Component)children.get(i)).collect();
} }
public class
VisitorComposite2 {
public static
void main( String[] args ) {
Composite[] containers = new Composite[3];
for (int i=0; i < containers.length;
i++) {
containers[i] = new
Composite();
for (int j=1; j
< 4; j++)
containers[i].add( new Leaf( i * containers.length + j ) );
}
for (int i=1; i < containers.length; i++)
containers[0].add( containers[i]
);
containers[0].traverse();
System.out.println();
containers[0].collect();
System.out.print( "letters are - " + Component.getLetters()
);
System.out.println( ",
numbers are - " + Component.getNumbers() );
} }
// a 1 2 3
b 4 5 6 c 7 8 9
// letters are - abc, numbers are - 123456789
//------------------------------
VisitorComposite3 ------------------------------
import
java.util.*;
interface Component {
void traverse();
void accept( Visitor v );
}
class Leaf implements
Component {
private int
number;
public Leaf( int num
) { number = num; }
public void traverse() { System.out.print( number + "
" ); }
public void accept(
Visitor v ) { v.visit( this ); }
public int getNumber() { return number; }
}
class
Composite implements Component {
private static char next = 'a';
private List children = new ArrayList();
private char letter = next++;
public void add( Component c ) {
children.add( c ); }
public void
traverse() {
System.out.print(
letter + " " );
for
(int i=0; i < children.size(); i++)
((Component)children.get(i)).traverse();
}
public void accept( Visitor v ) {
v.visit( this );
for (int i=0; i < children.size(); i++)
((Component)children.get(i)).accept( v );
}
public char getLetter() { return letter; }
}
interface
Visitor {
void visit( Leaf l
);
void visit( Composite c
);
}
class CollectVisitor implements Visitor {
private StringBuffer letters = new
StringBuffer();
private
StringBuffer numbers = new StringBuffer();
public void visit( Composite c ) { letters.append( c.getLetter() ); }
public void visit( Leaf l ) {
numbers.append( l.getNumber() ); }
public String getLetters()
{ return letters.toString(); }
public String getNumbers()
{ return numbers.toString(); }
}
public class
VisitorComposite3 {
public static
void main( String[] args ) {
Composite[] containers = new Composite[3];
for (int i=0; i < containers.length;
i++) {
containers[i] = new
Composite();
for (int j=1; j
< 4; j++)
containers[i].add( new Leaf( i * containers.length + j ) );
}
for (int i=1; i < containers.length; i++)
containers[0].add( containers[i]
);
containers[0].traverse();
System.out.println();
CollectVisitor anOperation = new CollectVisitor();
containers[0].accept( anOperation );
System.out.print( "letters are -
" + anOperation.getLetters() );
System.out.println( ", numbers are - " +
anOperation.getNumbers() );
} }
// a 1 2 3 b 4 5 6 c 7 8
9
// letters are - abc, numbers are - 123456789
//===============================================================================
//
Problem. "If you want to add a new
Visitable object, you have to change
// the Visitor interface, and then
implement that method in each of your
// Visitors."
//
//
Solution. With the ReflectiveVisitor,
you only need one method in the
// Visitor interface - visit(Object). All other visit() methods can be
//
added later as point-to-point coupling is required.
import
java.lang.reflect.Method;
////////// The "element"
hierarchy //////////
interface Element {
public void accept( ReflectiveVisitor v
);
}
class This implements Element {
public void accept(
ReflectiveVisitor v ) { v.visit( this ); }
public String thiss() { return "This"; }
}
class
That implements Element {
public
void accept( ReflectiveVisitor v ) {
v.visit( this ); }
public String
that() { return
"That"; }
}
class TheOther implements Element {
public void accept( ReflectiveVisitor v ) { v.visit( this ); }
public String theOther() { return
"TheOther"; }
}
////////// The
"operation" hierarchy //////////
abstract class
ReflectiveVisitor {
abstract public
void visit( Object o );
public void visitTheOther( TheOther e ) {
System.out.println(
"ReflectiveVisitor: do Base on " + e.theOther() );
}
// 1. Look for visitElementClassName() in the current class
// 2. Look for visitElementClassName() in
superclasses
// 3. Look for
visitElementClassName() in interfaces
// 4. Look for visitObject() in current class
protected Method getMethod( Class c )
{
Class newc = c;
Method m =
null;
while (m == null &&
newc != Object.class) {
String method = newc.getName();
method = "visit" + method.substring(
method.lastIndexOf('.') + 1 );
try {
m =
getClass().getMethod( method, new Class[] { newc } );
} catch (NoSuchMethodException ex)
{
newc =
newc.getSuperclass();
} }
if (newc == Object.class) {
// System.out.println( "Searching
for interfaces" );
Class[] interfaces = c.getInterfaces();
for (int i=0; i < interfaces.length; i++) {
String method = interfaces[i].getName();
method = "visit" +
method.substring( method.lastIndexOf('.') + 1 );
try {
m = getClass().getMethod( method, new Class[] {
interfaces[i] } );
} catch
(NoSuchMethodException ex) { }
} }
if (m == null)
try {
m = getClass().getMethod( "visitObject", new
Class[] { Object.class } );
}
catch (Exception ex) { }
return
m;
} }
class UpVisitor extends ReflectiveVisitor {
public void visit( Object o ) {
try {
getMethod( o.getClass() ).invoke( this, new Object[] { o }
);
} catch (Exception ex)
{
System.out.println(
"UpVisitor - no appropriate visit() method" );
} }
public void visitThis( This e ) {
System.out.println( "UpVisitor: do Up on " +
e.thiss() );
}
public void visitObject( Object e ) {
System.out.println( "UpVisitor:
generic visitObject() method" );
} }
class DownVisitor
extends ReflectiveVisitor {
public void visit( Object o ) {
try {
getMethod(
o.getClass() ).invoke( this, new Object[] { o } );
} catch (Exception ex) {
System.out.println( "DownVisitor -
no appropriate visit() method" );
} }
public void visitThat(
That e ) {
System.out.println(
"DownVisitor: do Down on " + e.that() );
} }
class
VisitorDemo {
public static void
main( String[] args ) {
Element[] list = { new This(),
new That(), new TheOther() };
UpVisitor up = new UpVisitor();
DownVisitor down = new DownVisitor();
for (int i=0; i < list.length; i++)
list[i].accept( up );
for (int i=0; i < list.length;
i++)
list[i].accept( down
);
} }
// UpVisitor: do Up on This
// UpVisitor: generic
visitObject() method
// ReflectiveVisitor: do Base on TheOther
//
DownVisitor - no appropriate visit() method
// DownVisitor: do Down on
That
// ReflectiveVisitor: do Base on TheOther