Is the following java implementation of the visitor pattern using generics, general enough to be useful? (I suppose it is).
Could it be improved in some way? It's important to be easily call-able using anonymous classes. Thanks.
(Example of use):
Vector<Number> numbers = new Vector<Number>();
numbers.add(new Double(1.2));
numbers.add(new Float(-1.2));
numbers.add(new Double(4.8));
numbers.add(new Float(-3.4));
numbers.add(new Long(123456));
numbers.add(new Short("14"));
For.each(numbers, new Visitor<Number>() {
public void doIt(Double n) {
System.out.println("doIt() for double: " + n);
}
public void doIt(Float n) {
System.out.println("doIt() for float: " + n);
}
public void doIt(Number n) {
System.out.println("doIt() for Number: " + n);
}
});
Visitor<Number> visi = new Visitor<Number>() {
private StringBuffer all = new StringBuffer ();
public void doIt(Number n) {
System.out.println("doIt() for Number: " + n);
all.append(n.toString() + " ");
}
public Object getResult () {
return all;
}
};
For.each(numbers, visi);
System.out.println ("all -> " + visi.getResult());
Definitions:
//............................................
abstract class Visitor<T> {
public void visit(T n) {
try {
this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n);
} catch (Exception ex) {
doIt((T) n);
}
}
public void doIt(T n) {
System.out.println("doIt() for base " + n);
}
public Object getResult() {
return null;
}
} // class
//............................................
class For {
public static <T> void each (Collection<T> c, Visitor<T> f) {
for (T v : c) {
f.visit(v);
}
} // ()
} // class
This is not the Visitor Pattern.
Visitor is characterized by the visitee having an accept(Visitor v)
method that interacts with an visit method in the visitor taking as parameter the visitee and overloaded for the varying type of the visitee, forming a "double dispatch" mechanism.
Quoting from the "Applicability" section for Visitor in Design Patterns:
Use the Visitor pattern when
-
an object structure contains many
classes of objects with differing
interfaces, and you want to perform
operations on these objects that
depend on their concrete classes.
-
many distinct and unrelated operations need to be performed on
objects in an object structure, and
you want to avoid "polluting" their
classes with these operations.
Visitor lets you keep related
operations together by defining them
in one class. When the object
structure is shared by many
applications, use Visitor to put
operations in just those applications
that need them.
-
the classes defining the object structure rarely change, but you
often want to define new operations
over the structure. Changing the
object structure classes requires
redefining the interface to all
visitors, which is potentially
costly. If the object structure
classes change often, then it's
probably better to define the
operations in those classes.
So this pattern is for dealing with similar opertaions on objects of multiple types. In your examples the objects you're calling visitors can only deal with one type.
In your answer revising to use reflection to handle multiple types (which by the way would be better done as an edit to the question or as a separate question), you're avoiding creating an accept(Visitor v)
method in the visited classes by using reflection, which is to a degree accomplishing the same goal, but somewhat awkwardly. I still would resist calling it an implementation of Visitor.
If code in the style you've written here is useful to you, by all means use it, but please don't call it a Visitor.
This is more like a Strategy Pattern or a Function Object, and if you rename the generic class in a way that reflects that, it's in fact useful, and your usage is similar to common patterns of list handling in functional languages.
What I would likely do with the code from the question is rename your Visitor<T>
to Operation<T>
and rename your visit(T t)
to execute(T t)
or apply(T t)
, thinking of an Operation
as a Function
without a return value. I have in fact used exactly this in ways similar to what you're doing, and used similar tactics for collection "mapping" using generic Function<Domain, Range>
objects. I'm not sure what pattern name actually fits it, but it's not Visitor. It's bringing functional list-comprehension style to an OO world where functions are not naturally first-class objects.
Thanks to the answer of donroby about my initial code not implementing the visitor pattern I came to this new version.
I suppose it now implements the visitor pattern without the need of modifying the visited element with an accept() method. Anyway, it is able to call the right method depending on the element type (I guess that's the mission for the accept()), thanks to reflection.
First, an example of use:
Vector<Number> numbers = new Vector<Number>();
numbers.add(new Double(1.2));
numbers.add(new Float(-1.2));
numbers.add(new Double(4.8));
numbers.add(new Float(-3.4));
numbers.add(new Long(123456));
numbers.add(new Short("14"));
For.each(numbers, new Visitor<Number>() {
public void doIt(Double n) {
System.out.println("doIt() for double: " + n);
}
public void doIt(Float n) {
System.out.println("doIt() for float: " + n);
}
public void doIt(Number n) {
System.out.println("doIt() for Number: " + n);
}
});
That produces this output
doIt() for double: 1.2
doIt() for float: -1.2
doIt() for double: 4.8
doIt() for float: -3.4
doIt() for Number: 123456
doIt() for Number: 14
And finally the code
abstract class Visitor<T> {
public void visit(T n) {
try {
this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n);
} catch (Exception ex) {
doIt((T) n);
}
}
public void doIt(T n) {
System.out.println("doIt() for base " + n);
}
public Object getResult() {
return null;
}
}
class For {
public static <T> void each (Collection<T> c, Visitor<T> f) {
for (T v : c) {
f.visit(v);
}
} // ()
}
how about
for(String s : words)
System.out.println (s.toUpperCase());
int total = 0;
for(String s : words)
total = total + s.length();
System.out.println (" sum of lengths = " + total);