DevOps Zone is brought to you in partnership with:

Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 231 posts at DZone. You can read more from them at their website. View Full User Profile

The Visitor Design Pattern

04.28.2014
| 2911 views |
  • submit to reddit

 

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 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 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.



Published at DZone with permission of Nicolas Frankel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)