DEV Community

Vicente Maldonado
Vicente Maldonado

Posted on • Originally published at Medium on

1

Visitor Pattern in Java

When I find a concept difficult to understand I try to strip it to bare essentials. This happened to me recently with the visitor pattern so here is my take on it. Of course I will be grateful for any corrections. Here goes.

Let’s say we have three classes derived from a common parent, called A

abstract class A
{
    public String name;
    abstract void accept(Visitor v);
}
Enter fullscreen mode Exit fullscreen mode

class B that has two objects as components:

class B extends A
{
    public A child1;
    public A child2;

    public B(String name)
    {
        this.name = name;
    }

[@Override](http://twitter.com/Override)
    void accept(Visitor v)
    {
        v.visitB(this);
    }
}
Enter fullscreen mode Exit fullscreen mode

class C that has one component:

class C extends A
{
    public A child;

    public C(String name)
    {
        this.name = name;
    }

[@Override](http://twitter.com/Override)
    void accept(Visitor v)
    {
        v.visitC(this);
    }
}
Enter fullscreen mode Exit fullscreen mode

and class D that has no components

class D extends A
{
    public D(String name)
    {
        this.name = name;
    }

[@Override](http://twitter.com/Override)
    void accept(Visitor v)
    {
        v.visitD(this);
    }
}
Enter fullscreen mode Exit fullscreen mode

All three classes expose a property, name that lets us distinguish their instances and a method named accept that allows visitors to visit them. The classes don’t care and don’t need to know what their visitors do. Visitor is an interface:

interface Visitor
{
    public void visitB(B b);
    public void visitC(C c);
    public void visitD(D d);
}
Enter fullscreen mode Exit fullscreen mode

There is a method for each class it visits. Let’s try this out with a visitor implementation that just prints out the name of objects it visited:

class PrintVisitor implements Visitor
{
    public void visitB(B b)
    {
        b.child1.accept(this);
        System.out.println(b.name + " visited.");
        b.child2.accept(this);
    }

    public void visitC(C c)
    {
        System.out.println(c.name + " visited.");
        c.child.accept(this);
    }

    public void visitD(D d)
    {
        System.out.println(d.name + " visited.");
    }
}
Enter fullscreen mode Exit fullscreen mode

The visitor is recursive: it visits a tree node and then it visits its children. Now let’s make a tree made up from the classes B, C and D:

        /\*
            F
          / \
        B G
      / \ \
     A D H
         / \ \
        C E I

        \*/
Enter fullscreen mode Exit fullscreen mode

There are nine objects and seven relations. First, create the objects:

        B f = new B("F");
        B b = new B("B");
        B d = new B("D");

        C g = new C("G");
        C h = new C("H");

        D a = new D("A");
        D c = new D("C");
        D e = new D("E");
        D i = new D("I");
Enter fullscreen mode Exit fullscreen mode

Next, the relations:

        f.child1 = b;
        f.child2 = g;

        b.child1 = a;
        b.child2 = d;

        d.child1 = c;
        d.child2 = e;

        g.child = h;
        h.child = i;
Enter fullscreen mode Exit fullscreen mode

And finally start visiting our tree by visiting its root node:

        PrintVisitor v = new PrintVisitor();
        f.accept(v);
Enter fullscreen mode Exit fullscreen mode

The output is:

A visited.
B visited.
C visited.
D visited.
E visited.
F visited.
G visited.
H visited.
I visited.
Enter fullscreen mode Exit fullscreen mode

If you look at this article, the above code performs tree traversal, and what is called in-order traversal at that (). Let’s change our visitor class to do a pre-order traversal — the visitor first displays the node name and then visits its children:

class PrintVisitor implements Visitor
{
    public void visitB(B b)
    {
        System.out.println(b.name + " visited.");
        b.child1.accept(this);
        b.child2.accept(this);
    }

    public void visitC(C c)
    {
        System.out.println(c.name + " visited.");
        c.child.accept(this);
    }

    public void visitD(D d)
    {
        System.out.println(d.name + " visited.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Now the output is:

F visited.
B visited.
A visited.
D visited.
C visited.
E visited.
G visited.
H visited.
I visited.
Enter fullscreen mode Exit fullscreen mode

In post-order traversal the visitor first visits node children and only then displays its name:

class PrintVisitor implements Visitor
{
    public void visitB(B b)
    {
        b.child1.accept(this);
        b.child2.accept(this);
        System.out.println(b.name + " visited.");
    }

    public void visitC(C c)
    {
        c.child.accept(this);
        System.out.println(c.name + " visited.");
    }

    public void visitD(D d)
    {
        System.out.println(d.name + " visited.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Here is the output:

A visited.
C visited.
E visited.
D visited.
B visited.
I visited.
H visited.
G visited.
F visited.
Enter fullscreen mode Exit fullscreen mode

Besides the Wikipedia article I linked at the beginning, there is a nice description of the visitor pattern here. In short:

  • The visited objects don’t need to know what their visitors do, they just need to accept them.
  • There needs to be a protocol that lets visited objects and visitors communicate, in our case the Visitor interface.
  • A visitor uses separate methods (ie visitB, visitC and visitD for visiting each class)

(You can find the code on Github.)

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Instrument, monitor, fix: a hands-on debugging session

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️