public class MyVisitor extends ReflectiveSuperVisitor {
public void visit(HTMLTag tag) {
// do something
}
}
pass this to any object that accepts visitors and have it just run that code on anything that matches a HTMLTag. It's just a generic visitor pattern with the added coolness that there is no compile-time linking so there is no need to implement a particular visitor interface or have the model know about what visitors will visit it. In my mind, this is how the visitor pattern should be. Otherwise, it becomes too tedious for its own good in large systems.
I cache the runtime method lookups (essentially the same lookup that Java does at compile time to link to the right method) so that part isn't that intensive over many calls, but you still have to deal with a reflective method invocation. Because this is so core to all the transformations and feature extraction in webseer, I knew this had to be fundamentally faster for anyone to take it seriously.
A while back I read this great article that describes how to convert reflective calls into runtime compiled method calls. It fit perfectly and with about 15 lines of code of Javassist I was able to dramatically cut down the time of method invocation. I didn't get quite as dramatic speedups as his results (he was doing reflective lookups and several method invocations and it's possible that JVMs are faster at this now), but in my simple test it more than doubled the speed of the calls. I also changed the method invocations to take both the visitor and the acceptor so there can be one for each class in a static cache as opposed to per visitor like it was before - this cuts down both on memory overhead as well as GC time. Essentially I now dynamically generate classes that look like this:
public class MyVisitorvisitHTMLTag implements MethodCaller {
public void callFor(ReflectiveSuperVisitor visitor, Acceptor tag) {
((MyVisitor)visitor).visit((HTMLTag)tag);
}
}
Now when you call visitor.visit(this); in an accept(SuperVisitor) method, after the first lookup, it performs the following steps:
- invoke ReflectiveSuperVisitor.visit(Acceptor) - SUPER FAST
- get hashcode of runtime visitor class - SUPER FAST
- lookup MethodCaller in hashtable - FAST
- invoke MethodCaller - SUPER FAST
- invoke correct visit method - SUPER FAST
- invoke correct visit method - SUPER FAST