Posts Tagged ‘patterns’

Groovy multimethods and the visitor pattern

Posted in Code on September 23rd, 2011 by ataylor284 – 4 Comments

Visitor is one of the classic GOF patterns. It’s a great pattern for traversing and operating on objects. It’s particularly useful when your application is centered around a big data structure, like a compiler’s abstract syntax tree, or a browser’s document object model.

Java Implementation

The usual Java implementation needs a bit of code to work around how Java chooses methods. Java resolves every method call by the runtime type of the owning object, plus the types of the parameters. But when it considers the parameters, it uses their declared classes.

Below is a common implementation of the pattern.

When visit is called directly in Java, then instances of B or C will be dispatched to the wrong visit method. So an accept method is added to every visited class. accept simply calls visit on the visitor with this as a parameter. It can’t be inherited, since it’s purpose is to call visit with the right declared type.

import java.util.*;

public class JavaVisitor implements ABCVisitor {

    public void visit(Collection<A> collection) {
        for (A a : collection) {
            //visit(a);       // outputs A A A
            a.accept(this);   // outputs A B C
        }
    }

    public void visit(A x) {
        System.out.println("A");
    }

    public void visit(B x) {
        System.out.println("B");
    }

    public void visit(C x) {
        System.out.println("C");
    }

    public static void main(String[] args) {
        JavaVisitor v = new JavaVisitor();
        List<A> alist = new ArrayList<A>();
        alist.add(new A());
        alist.add(new B());
        alist.add(new C());

        v.visit(alist);
    }
}

interface ABCVisitor {
    void visit(A x);
    void visit(B x);
    void visit(C x);
}

class A { 
    void accept(ABCVisitor visitor) {
        visitor.visit(this);
    }
}

class B extends A {
    void accept(ABCVisitor visitor) {
        visitor.visit(this);
    }
}

class C extends B {
    void accept(ABCVisitor visitor) {
        visitor.visit(this);
    }
}

Groovy Implementation

One of the overlooked features of groovy is multiple method dispatch, or multimethods. Groovy resolves method calls by the actual parameter types, determined at runtime.

This just happens to make visitor a lot easier to implement. The double dispatch through accept is completely unnecessary. Unlike Java, visit can just be called directly.

class GroovyVisitor implements Runnable {

    void visit(Collection collection) {
        for (a in collection) {
            visit(a)
        }
    }

    void visit(A x) {
        println("A")
    }

    void visit(B x) {
        println("B")
    }

    void visit(C x) {
        println("C")
    }

    void run() {
        visit([new A(), new B(), new C()])
    }
}

class A { 
}

class B extends A {
}

class C extends B {
}

Now the classes being visited don’t need to follow any specific protocol. They can be any classes at all, including third party library classes not designed to be visited.

The ABCVisitor interface, which has to enumerate all the potential visited classes, is completely unnecessary. The code is clear and to the point, and completely encapsulated in the visitor class. It’s short, concise, without any unnecessary implementation details just like good groovy code should be.