Visitor Pattern

<< What is an Algebraic DataType? | How Can Algebraic DataTypes be Used? >>

This page gives background information on the Visitor Pattern. Most of the code on this page mimics code that jADT generates for you. If you feel comfortable with Visitors skip ahead to How to Use ADTs.

First, a quote from the Wikipedia article on Visitor.

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to easily follow the open/closed principle.

Ignoring how it's implemented, Visitor's goal is very similar to Java's switch statement. Given a primitive (int, long, char, etc.) switch allows you to take different action based on which value of type int/long/char/whatever you happen to have. The decision can be made without adding code to a base int/long/char class (indeed, there are no such classes). Visitor allows the same kind of dispatching to be added to a class hierarchy where the dispatch decision is typically based on the specific subclass found at runtime.

An example Visitor should help - we'll re-implement a method that uses switch to use a Visitor pattern to tie all the concepts together. We'll re-implement this enum and later create a switch replacement.

public enum ColorEnum {
    Red, Green, Blue;
}

First, we need an replacement for the enum. Ignore the _switch methods for now.

public interface Color {
    public void _switch(ColorSwitchBlock switchBlock);
}
public class Red implements Color {
    @Override
    public void _switch(ColorSwitchBlock switchBlock) {
        switchBlock._case(this);
    }

}
public class Blue implements Color {

    @Override
    public void _switch(ColorSwitchBlock switchBlock) {
        switchBlock._case(this);
    }

}
public class Green implements Color {
    @Override
    public void _switch(ColorSwitchBlock switchBlock) {
        switchBlock._case(this);
    }

}

So a Color is either a Red, a Green, or a Blue. Technically a Color could be any other subclass that anybody wants to write, but the Visitor pattern effectively prevents such subclassing as you'll see shortly.

The first half of the visitor pattern is the abstract _switch method in the base interface or class and concrete implementations in each subclass. The second half is the visitor interface

public interface ColorSwitchBlock {
    public void _case(Red x);
    public void _case(Blue x);
    public void _case(Green x);
}

So anything that implements the ColorSwitchBlock interface can be passed to the _switch method on a Color. That _switch method will call _case(Red), _case(Green), or _case(Blue) on the SwitchBlock. Hence dispatch based on the subclass. That's also why the Visitor pattern effectively prevents adding new subclasses. While it's possible to create class MyOtherColor implemenets Color, the only way to make it useful is to go back and add a _case(MyOtherColor) to ColorSwitchBlock and all its implementations.

All the Java code you've seen up to now is very similar to the Java that jADT generates. Although jADT seals ADTs against further subclassing and generates visitor variations that allow a useful return a useful result and Visitor variations that do default computations so you don't need to implement visit methods for every possible case.

As promised, we can finally implement an equivalent of a switch statement. We'll create something similar to this simple method.

public void printString(ColorEnum color, PrintWriter writer) {
    switch(color) {
    case Red:
        writer.print("red");
        break;
    case Green:
        writer.print("green");
        break;
    case Blue:
        writer.print("blue");
        break;
    }
}

Using the Color interface instead of ColorEnum and implementing a ColorSwitchBlock results in this equivalent method

public void printString(Color color, final PrintWriter writer) {
    color._switch(new ColorSwitchBlock() {            
        @Override
        public void _case(Red x) {
            writer.print("red");
        }
        
        @Override
        public void _case(Green x) {
            writer.print("green");                
        }
        
        @Override
        public void _case(Blue x) {
            writer.print("blue");
        }           
    });
}

There's a bit more boilerplate - it's hard to compete with a built in sugar like switch and enum - but the idea is captured.

While switch covers a lot of territory, two more dispatch mechanisms in Java are if and the ternary operator x?y:z, both of which dispatch on booleans. The ternary operator, unlike switch and if, is an expression instead of a statement meaning it returns a useful value that can be used in larger expressions. Similar to the if vs ternary operator distinction, jADT has visitor variations that that can be used in expressions or others that are only called for their effects such as modifying a shared data structure, writing to the network, or printing to the screen. Like Java's switch, it's possible to have a default case in a jADT Visitor.

<< What is an Algebraic DataType? | How Can Algebraic DataTypes be Used? >>