Visitor Pattern

Situation

Our working data are defined in a few classes. These classes have some relations, for example a class may be a subclass of another one, or one object may contain other objects.

We want to perform some actions on these data; for example copy them, save into file, nicely display on the screen, make a calculation, etc. Some actions may be subclasses of others.

To keep the program parts separated, we do not want to write the action code into data classes. We further want to be able to add new action without modifying data classes.

Notes

We need to dynamically call the right method, depending on (1) what action are we going to do, and (2) which data object are we going to do it on. This is called "double dispatch".

Most of object-oriented programming languages support only dynamic calling based on one object. For example:

class Data {
  ...
}


class Extended_Data extends Data {
  ...
}


interface Action {

  void handle(Data data);
  void handle(Extended_Data data);

}


class Simple_Action implements Action {

  void handle(Data data) {
    System.out.println("Simply handling data.");
  }

  void handle(Extended_Data data) {
    System.out.println("Simply handling extended data.");
  }

}


class Difficult_Action extends Simple_Action {

  void handle(Data data) {
    System.out.println("Difficultly handling data.");
  }

  void handle(Extended_Data data) {
    System.out.println("Difficultly handling extended data.");
  }

}


class Main_Program {

  public static void main() {
    Action a1 = new Simple_Action();
    Action a2 = new Difficult_Action();
    Data d1 = new Data();
    Data d2 = new Extended_Data();
    a1.handle(d1);  // right: Simply handling data.
    a1.handle(d2);  // WRONG: Simply handling data.
    a2.handle(d1);  // right: Difficultly handling data.
    a2.handle(d2);  // WRONG: Difficultly handling data.
  }

}

In this situation the called method is dynamically chosen depending on the action type. Even though the variables "a1" and "a2" are of the same type "Action", the method is selected by the object currently assigned to the variable; and because one of them contains "Simple_Action" and another contains "Difficult_Action", the respective methods are called. But when selecting one of two possible "handle" functions, only the variable type is used; and because variable "d2" has variable type "Data", the method for "Data" is called, although the variable is containing the "Extended_Data" object.

If we would change the program so that instead of actions having method "handle" we would give data the method "handle_me" with action as a parameter, we would have received the opposite situation. The method choice would depend dynamically on data, but statically on action. (And by the way, we do not want to do this, because we do not want to modify the data code on adding a new action.)

Solution

To make the method dynamically called also depending on the data type, we will add to data new function "handle_me", which will dynamically call the respective function of the "Action" interface. Because this method is related to interface, not to action classes, we add it only once, regardless of how many actions we have.

class Data {

  void handle_me(Action action) {
    action.handle(this);
  }

  ...

}


class Extended_Data extends Data {

  void handle_me(Action action) {
    action.handle(this);
  }

  ...

}


interface Action {

  void handle(Data data);
  void handle(Extended_Data data);

}


class Simple_Action implements Action {

  void handle(Data data) {
    System.out.println("Simply handling data.");
  }

  void handle(Extended_Data data) {
    System.out.println("Simply handling extended data.");
  }

}


class Difficult_Action extends Simple_Action {

  void handle(Data data) {
    System.out.println("Difficultly handling data.");
  }

  void handle(Extended_Data data) {
    System.out.println("Difficultly handling extended data.");
  }

}


class Main_Program {

  public static void main() {
    Action a1 = new Simple_Action();
    Action a2 = new Difficult_Action();
    Data d1 = new Data();
    Data d2 = new Extended_Data();
    d1.handle_me(a1);  // right: Simply handling data.
    d2.handle_me(a1);  // right: Simply handling extended data.
    d1.handle_me(a2);  // right: Difficultly handling data.
    d2.handle_me(a2);  // right: Difficultly handling extended data.
  }

}

This is the basic idea, the rest are technical improvements. If we want methods to return values and/or throw exceptions, we can use the following headings:

Object handle_me(Action action) throw Exception;

Object handle(... data) throw Exception;

Google