Dynamic Visitor Pattern in Java

The Visitor Pattern is a great way to extend the functionality of a class without extending the class per se. The gory details are very well explained on Wikipedia, but it works by a visiting class, the “Visitor,” calling a second class that “accepts” the Visitor. Once the Visitor is accepted, the second class reveals its type by calling a specific, typed method on the Visitor. Now that the Visitor knows the type of the second class, it can behave in a highly specific way that, in effect, extends the behavior of the second class. The Wikipedia page has good examples of the basic pattern implemented in multiple languages, including Java.

The Visitor Pattern has one major downside: The exact list of classes that can be visited is completely specified on the Visitor interface. This presents a problem if a new class needs to be visited that is not available on the interface. The easiest solution is to add the new class to the the interface, but that is not possible with third party code – downstream developers in general do not modify API that they then call.

Dynamic Visitors for New Types

So how can developers allow visitation for other classes or subclasses without requiring extension of the primary Visitor interface? The trick is to separate the act of accepting the Visitor from from the final visitation by way of a delegate. The code looks something like the following, which is taken from the Eclipse January project where I committed it yesterday:

/**
 * A simple, templated Visitor interface as part of the Visitor pattern. This
 * interface is implemented by classes that work with IVisitorHandlers to
 * dynamically and generically extend the visitation capabilities of the forms
 * package. Using this interface in place of the IComponentVisitor interface
 * allows clients to create custom Components or other structures and visit
 * them dynamically, wherease the IComponentVisitor interface is static and does
 * not allow extension outside the basic components.
 *
 * @author Jay Jay Billings
 *
 */
public interface IVisitor<T> {

	/**
	 * This operation directs the visitor to visit the provided data element.
	 * @param component The data element that should be visited.
	 */
	public void visit(T element);

}

/**
 * This is a simple interface for registering visitors under a generic visitor
 * pattern. It is designed such that implementers should use the IVisitors that
 * are registered with the set() operation to visit the objects passed to the
 * visit() operation. This allows run-time registration of generic visitation
 * callbacks without the need for a verbose, static interface such as
 * IComponentVisitor. Registration is as simple as associating a Class with an
 * implementation of IVisitor<T>.
 *
 * This class should not be used in general for all the data types in Forms. It
 * is better to implement IComponentVisitor or extend SelectiveComponentVisitor
 * in those cases because it minimizes the code and avoid bugs. This class and
 * the IVisitor interface are meant to be used only for classes that are not
 * already available on those two entities.
 *
 * @author Jay Jay Billings
 */
public interface IVisitHandler {

	/**
	 * This operation associates an IVisitor with a Class.
	 * @param classType The Class that should be associated with the Visitor
	 * @param visitor The IVisitor that will be invoked for the given class.
	 */
	public void set(Class classType, IVisitor visitor);

	/**
	 * This operation uses the registered IVisitor to visit the injected
	 * object.
	 * @param objectToVisit The object that should be visited.
	 */
	public void visit(Object objectToVisit);

}

/**
 * This interface defines a visitation contract where visitation requests are
 * granted through a delegate provided by an IVisitHandler. This interface is an
 * alternative to IVisitable for classes that may need to execute visitation code
 * for classes not available on the IComponentVisitor interface.
 *
 * @author Jay Jay Billings
 */
public interface IGenericallyVisitable {

	/**
	 * This operation will accept a visit handler instead of a typed visitor
	 * that will then be called as a delegate for direct visitation.
	 * @param visitHandler
	 */
	public void accept(IVisitHandler visitHandler);

}

This works by providing a second interface – IGenericallyVisitable – that classes can realize to accept a delegate – the IVisitHandler – instead of directly accepting the Visitor. This allows the IVisitHandler to smoothly direct visitation to a generic IVisitor<T> that is typed specifically for a new class that is not part of the original Visitor interface.

The January repo has a good example showing how this works. All of the code there and here is licensed under the Eclipse Public License version 1.0.

Drawbacks

There are a couple of downsides with this approach that may keep it from working for all projects. First and foremost, the compile type checking provided by the full visitor interface is not present for a generic visitor interface and it is possible that run time case exceptions could occur because the proper visit() operation is not available. In that case the error looks like the following:


java.lang.ClassCastException: org.eclipse.ice.datastructures.test.TestClass2 cannot be cast to org.eclipse.ice.datastructures.test.TestClass
at org.eclipse.ice.datastructures.test.TestVisitor3.visit(BasicVisitHandlerTester.java:1)
at org.eclipse.january.form.BasicVisitHandler.visit(BasicVisitHandler.java:48)
at org.eclipse.ice.datastructures.test.TestClass2.accept(BasicVisitHandlerTester.java:24)
at org.eclipse.ice.datastructures.test.BasicVisitHandlerTester.testVisit(BasicVisitHandlerTester.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at

...

In C++, this can be handled a little differently compared to Java because of Explicit Specialization of function templates… which would have been so much nicer to have than resorting to a basic handle by using the Object class.

Second, the exact means by which the delegation to the IVisitHandler happens most likely requires either upfront registration or hardwiring in IVisitHandler implementations. In this case, January goes with upfront registration, which actually isn’t too bad.

Finally, delegation always comes with a performance hit if it isn’t handled efficiently.

Comments are welcome to my Twitter account! @jayjaybillings

 

Advertisements

NVIDIA CUDA 7.5 on Fedora 23 with NVIDIA Optimus Technology

I have a Dell XPS 15 that has both Intel and NVIDIA graphics using NVIDIA’s Optimus Technology:

00:02.0 VGA compatible controller: Intel Corporation HD Graphics 530 (rev 06)
01:00.0 3D controller: NVIDIA Corporation GM107M [GeForce GTX 960M] (rev a2)

Getting CUDA 7.5 to work was straightforward using Bumblebee, as described on the Fedora Bumblebee Page. I used the managed proprietary option to get the latest NVIDIA driver for my system. Using both optirun and primusrun works, however on glxgears they report different numbers

$ optirun glxgears
10162 frames in 5.0 seconds = 2032.270 FPS

$ primusrun glxgears
292 frames in 5.0 seconds = 58.370 FPS
primus: warning: dropping a frame to avoid deadlock
primus: warning: timeout waiting for display worker

I installed the CUDA Toolkit using the dnf repository provided by NVIDIA. It is important to only install the Toolkit from this repository, not the version of CUDA that is provided since Bumblebee already configured an appropriate Optimus-ready NVIDIA driver. So, instead you can just run

sudo dnf install cuda-toolkit-7-5

Additional configuration works as specified in the User’s Manual and Getting Started Guides. I had trouble getting the examples to compile because Fedora 23, which I’m on, defaults to gcc 5.3.1 and, unfortunately, CUDA 7.5 requires gcc 4.9 or less. I compiled GCC 4.9 from scratch and then manually modified the makefiles of each sample I wanted to compile to point to “g++49” instead of “g++”. You’ll notice that I told the gcc configuration during build time to add the 49 suffix with the –program-suffix=49 option. Aside from that, compilation and execution were simple enough with two catches:

  • It was necessarily to override the library path to point to Bumblebee’s version of the cuda shared library instead of a 32-bit version that somehow ended up in /usr/lib.
  • Contrary to what the Bumblebee documentation suggests, you must use optirun or primusrun to execute the samples. I’m not sure if this is required on more basic programs yet.

So, that looks something like this for the nbody example:

LD_LIBRARY_PATH=/usr/lib64/nvidia-bumblebee primusrun ./nbody

cudaNBody_cropped