Dynamic Dispatcher Library

A nonintrusive, covariant replacement for applying the VISITOR pattern

class diagram explaining dynamic dispatcher

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

  • 5 Mai, 2004: We have released the 0.0.4 version of the dynamic dispatcher library. Runs out of the box. This will be the last "pre-release". The 0.1 version will soon follow!

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.

Don't hesitate to send us a mail, if you have any questions! We are very interested in your opinion about this project. You are very welcome to make comments, either by mail or using the sourceforge discussion forum.

How do I use this library? - Usage instructions:

To use a dynamic dispatcher instead of applying the VISITOR pattern do the following:

  • Write your Visitor class as usual, but do not introduce an accept() method in your domain classes (domain classes mean the classes the visitor operates on). It is not necessary to define a common visitor base class.
  • Instead create a dispatcher object - typically as a field in your visitor:
    private Dispatcher dsp = DispatcherFactory.createDefaultVisitor(this);
  • Don't write domainObj.accept(this); in your visit methods. Instead write dsp.dispatch(domainObj);
  • For convenience, define a method public doIt(DomainBaseClass arg) {dsp.dispatch(arg);} that users of you dispatcher can use.
  • or similar. The name should reflect what the visitor does.

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.

Performance

The 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:
  • Visibility: Currently, the visit methods must have public visibility. In general, package protected visitor classes should also be accessible. We are working on it. However, in most cases visitor classes in the original GoF pattern have public visibility anyway, because the implement a public visitor base interface which is needed by the accept methods.

Javadocs

The Javadocs of the 0.0.4 release are included in the distribution. They are also available online.

Examples

All 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 dump() in each AWT class C for which we now have a visit(C param) in the visitor. However, since the java.* and javax.* classes are sealed this is no option.

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 Example

The following example illustrates how this small library can be used. It consists of

  • A couple of "domain classes": Shape, Line, Circle, Picture, Arrow
  • Two new operations (scale and persist) which could be declared in Shape and overridden in Line, Circle, and Picture. Instead, as in the VISITOR pattern, we define them as their own classes (the visitors).
Because arrows are translated the same way as lines (if 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
	*/
  }

Download

For 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!
  • We believe that the dynamic dispatcher is very useful in some domains. What is your opinion? Did you use this library? Tell us your experiences with this library!
  • Contribute to the .NET implementation of the dynamic dispatcher.

Last modified: May 3, 2004 by Fabian Buettner