Home > Java > The Visitor design pattern

The Visitor design pattern

I guess many people know about the Visitor design pattern, described in the Gang of Four’s Design Patterns: Elements of Reusable Object-Oriented Software book. The pattern itself is not very complex (as many design patterns go):

Visitor UML class diagram

I’ve known Visitor since ages, but I’ve never needed it… yet. Java handles polymorphism natively: the method call is based upon the runtime type of the calling object, not on its compile type.

interface Animal {
    void eat();
}
public class Dog implements Animal {
    public void eat() {
        System.out.println("Gnaws bones");
    }
}

Animal a = new Dog();
a.eats(); // Prints "Gnaws bones"

However, this doesn’t work so well (i.e. at all) for parameter types:

public class Feeder {
    public void feed(Dog d) {
        d.eat();
    }
    public void feed(Cat c) {
        c.eat();
    }
}

Feeder feeder = new Feeder();
Object o = new Dog();
feeder.feed(o); // Cannot compile!

This issue is called double dispatch as it requires calling a method based on both instance and parameter types, which Java doesn’t handle natively. In order to make it compile, the following code is required:

if (o instanceof Dog) {
    feeder.feed((Dog) o);
} else if (o instanceof Cat) {
    feeder.feed((Cat) o);
} else {
    throw new RuntimeException("Invalid type");
}

This gets even more complex with more overloaded methods available – and exponentially so with more parameters. In maintenance phase, adding more overloaded methods requires reading the whole if stuff and updating it. Multiple parameters are implemented through embedded ifs, which is even worse regarding maintainability. The Visitor pattern is an elegant way to achieve the same, with no ifs, at the expense of a single method on the Animal class.

public interface Animal {
    void eat();
    void accept(Visitor v);
}

public class Cat {
    public void eat() { ... }
    public void accept(Visitor v) {
        v.visit(this);
    }
}

public class Dog {
    public void eat() { ... }
    public void accept(Visitor v) {
        v.visit(this);
    }
}

public class FeederVisitor {
    public void visit(Cat c) {
        new Feeder().feed(c);
    }
    public void visit(Dog d) {
        new Feeder().feed(d);
    }
}

Benefits:

  • No evaluation logic anywhere
  • Only adherence between Animal and FeederVisitor is limited to the visit() method
  • As a corollary, when adding new Animal subtypes, the Feeder type is left untouched
  • When adding new Animal subtypes, the FeederVisitor type may implement an additional method to handle it
  • Other cross-cutting logic may follow the same pattern, e.g. a train feature to teach animals new tricks

It might seem overkill to go to such lengths for some simple example. However, my experience taught me that simple stuff as above are fated to become more complex with time passing.

email
Send to Kindle
Categories: Java Tags:
  1. crg
    April 28th, 2014 at 05:46 | #1

    I’ve never used the visitor pattern. Should the last part be:

    class Feeder implements Visitor {
    void visit(Cat c) { c.eat(); }
    void visit(Dog d) { d.eat(); }
    }

    Thanks. If you add a Snake later, you should only need to add one method to the Feeder class, correct? What if you have too Animal types and Feeder becomes very large?

  2. Thomas
    April 28th, 2014 at 12:59 | #2

    Why not feed animals?

    Animal a = new Dog();
    feeder.feed(a);

  3. ako
    April 28th, 2014 at 14:39 | #3

    I don’t get the point of your example, your interface is enough already :

    public class Feeder {
    void feed(Animal a) {
    a.eat();
    }
    }

    public class FeederTest {

    @Test
    public void testFeeding() {
    Feeder feeder = new Feeder();
    feeder.feed(new Dog());
    feeder.feed(new Cat());
    feeder.feed(new Animal() {
    public void eat() {
    System.out.println(“Anonymous animal that eats bricks”);
    }
    });
    }
    }

    public class FeederTest {

    @Test
    public void testFeeding() {
    Feeder feeder = new Feeder();
    feeder.feed(new Dog());
    feeder.feed(new Cat());
    feeder.feed(new Animal() {
    public void eat() {
    System.out.println(“Anonymous animal that eats bricks”);
    }
    });
    }
    }

  4. ako
    April 28th, 2014 at 14:40 | #4

    sorry, with spaces it should look better :
    public class Feeder {
    void feed(Animal a) {
    a.eat();
    }
    }

    public class FeederTest {

    @Test
    public void testFeeding() {
    Feeder feeder = new Feeder();
    feeder.feed(new Dog());
    feeder.feed(new Cat());
    feeder.feed(new Animal() {
    public void eat() {
    System.out.println(“Anonymous animal that eats bricks”);
    }
    });
    }
    }

  5. April 29th, 2014 at 18:11 | #5

    The visitor pattern is one of the most exaggerated patterns. I’d say that in 80% of the cases where it is applied, it is applied because of an architecture astronaut wanting to show off with his pattern skills.

    In a more formal context, I have concluded that the visitor pattern is only OK-ish, when you have lots and lots of visitors, but only very few component types in your tree structure, i.e. only very few cats and dogs.

    Details here: http://blog.jooq.org/2012/04/10/the-visitor-pattern-re-visited/

  1. No trackbacks yet.