// 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