|
Dynamic Dispatcher LibraryA nonintrusive, covariant replacement for applying the VISITOR pattern
Overview
A Dynamic Dispatcher is a replacement for applying the VISITOR design pattern. It can be used to define new operations (visitors) over domain class hierarchies. Unlike VISITOR, this works without modifying the domain classes. In short, the accept method of the VISITOR pattern is replaced by an explicit dispatcher object. Furthermore, the dispatcher object simulates covariant method overriding. This means, a visitor class does not necessarily define visit methods for all domain classes. Instead, the most appropriate visit method is chosen during dispatch (in the above figure, the dispatcher for Translate will dispatch an Arrow parameter to visit(Line). For performance critical applications (e.g., when millions of invocations have to be dispatched), this library provides on the fly byte compiled dispatcher classes. These are even faster than the (already fast) generic, reflection based dispatcher implementation. The byte code generator uses the free Apache Byte Code Engineering Library (BCEL), which is included in the distribution. Currently, the library is only available for Java. We plan to implement it as a .NET component as well (contribute!). This project is hosted on sourceforge, click here to visit the project page. News
Docs
This project was initially based on
this
paper (to be published in the ACM proceedings of the
SEKE'04
conference).
A technical documentation will soon be available. Until then, have a look
in the Javadocs for further
descriptions. How do I use this library? - Usage instructions:To use a dynamic dispatcher instead of applying the VISITOR pattern do the following:
Some examples can be found below. If you uses the default (reflection based) dispatcher, you just have to include dynamicdispatcher.jar in you CLASSPATH. If you want to use byte compiled dispatcher classes, you must also include bcel.jar. PerformanceThe default, reflection based dynamic dispatcher already has a quiet good performance and should not produce real overhead in many situations. The byte compiled dispatcher is even faster, but creating the dispatcher takes a little more time. You can estimate how expensive using a dispatcher is. The distribution contains a performance test tool. See README for details. For example, on my single user PIII, 1.8Ghz, J2SDK 1.4.2, the costs for dispatching 100.000 method invocations on a domain class hierarchy of thirty classes takes 0.4 seconds. Using the a byte code generated dispatcher, 10.000.000 method invocations take 1.3 seconds. Known limitations:The following limitations apply to the current release:
JavadocsThe Javadocs of the 0.0.4 release are included in the distribution. They are also available online.ExamplesAll examples in this section are included in the current release.AWTDumper Example
An example of using a dynamic dispatcher for dumping a tree of
AWT components (Component,Containers,Buttons, and so on),
e.g. for debugging. It demonstrates how operations can be added to
closed frameworks: We could have implemented the dumping operation by creating a
method public class AWTDumper { private Dispatcher dsp = DispatcherFactory.createDefaultDispatcher(this); private int indent = 0; public void dump(Component c) { dsp.dispatch(c); } public void visit(Component component) { print("Component: " + component.getClass().toString()); } public void visit(Container container) { print("Container: " + container.getClass().toString()); ++indent; Component[] contents = container.getComponents(); for (int i=0;i< contents.length;++i) { dsp.dispatch(contents[i]); } --indent; } public void visit(Button button) { print("Button: " + button.getText()); } void print(String s) { for (int i=0;i< indent;++i) { System.out.print(" "); } System.out.println(s); } } Graphics ExampleThe following example illustrates how this small library can be used. It consists of
translate() would
be defined inside the Shape hierarchy it would not have been overridden in
Arrow), we do not provide a visit(Arrow a) method in Translate .
If we had applied the standard VISITOR pattern, this is not possible. We
had to explicitly define visit(Arrow a) which then could call
visit((Line)a) .
It can be seen, that we do not have to modify Shape and co. in order to realize translate. public abstract class Shape { } public class Line extends Shape { float x1,y1,x2,y2; // constructor ... } public class Circle extends Shape { float x,y,r; // constructor ... } public class Picture extends Shape { ArrayList elements = new ArrayList(); // pictures consist of Shapes // constructor ... } public class Arrow extends Line { // constructor ... } public class Translate { float x,y; // select the default dispatcher for "visit" methods Dispatcher dsp = DispatcherFactory.createDefaultDispatcher(this); public void dispatch(Shape s) { dsp.dispatch(s); } public void visit(Line l) { l.x1+=x;l.y1+=y; l.x2+=x;l.y2+=y; } public void visit(Circle c) { c.x+=x;c.y+=y; } public void visit(Picture p) { for (Iterator it=p.elements.iterator();it.hasNext();) { Shape s = (Shape)it.next(); dsp.dispatch(s); } } // no visit(Arrow), it is handled by visit(Line) !! } ... public static void main(String[] args) { Picture p = new Picture(); Arrow a = new Arrow(1.0,1.0,2.0,4.0); Circle c = new Circle(1.0,2.0,3.0); p.add(a);p.add(c); Translate t = new Translate(1.0,2.0); t.dispatch(p); /*Output: invoked visit(Picture) invoked visit(Line) <= ! invoked visit(Circle) a: x1=2.0,y1=3.0x2=3.0,y2=6.0 c: x=2.0,y=4.0r=3.0 */ } DownloadFor the latest download, please go to the project SourceForge pages. This is page contains the release version.Contribute!
Send us a mail
if you want to contribute to any of the following items.
General comments and bug reports are also very welcome!
|