next up previous
Next: 1.12.4 Polymorphic Visitors with Up: 1.12 The Visitor Pattern Previous: 1.12.2 Openness in Data

1.12.3 Polymorphic Visitors

In our application of the visitor pattern above, the for methods of the visitor classes and the accept methods for the ArithExpr classes returned values of type int. This convention is acceptable as long as all the computations we ever want to perform over ArithExprs have integer results. But if not, we are forced to declare a new interface visitor type and new accept methods for each distinct result type. Since the whole point of the visitor pattern is to avoid having to modify a data type every time we wish to perform some new computation over it, we have a potential problem.

We can address this problem by redefining the for and accept methods so that they return a more general type. Before getting into the details, let's step back and give visitors a more general definition.

A visitor is an object containing

The properties common to all visitors for a particular composite type are collected in corresponding the visitor interface. Any arguments that are processed by a particular visitor are typically stored in fields declared in the corresponding visitor subclass to avoid compromising the generality of the interface. A typical visitor interface has the form

interface Visitor {
  // a "for" method for each concrete
  // subclass of the visited class
  Object forC1(...);   
  .                              
  .
  .
  Object forCn(...);
}

We use Object as the return type of the for methods so that we can accommodate concrete visitor classes that produce almost any type of result. This convention exploits the polymorphism inherent in class inheritance: every object belongs to all of the types associated with its superclass, super-superclass, etc. Since the class Object is perched at the root of the class hierarchy, all objects belong to the type Object, and support the operations defined in class Object. The only types that do not belong to Object are the primitive types like int. Fortunately, we can get around this by using the corresponding container classes (e.g. Integer instead of int). In the event we want a visitor operation to have a void return type, we can use the container class for void called Void; the expression new Void() returns a value of this type. Alternatively, we can simply return the null reference of Object type.

Let us return to our ArithExpr example. We can generalize our visitor class as follows:

abstract class AE { // AE is short for ArithExpr
  abstract Object accept(Visitor v);
}

class Const extends AE {
  /* fields */
  int value;

  /* constructor */
  Const(int v) { value = v; }

  /* getters */
  int getValue() { return value; }

  /* toString */
  public String toString() {
    return Integer.toString(value);
  }

  Object accept(Visitor v) {
    return v.forConst(this);
  }
}

. . .

The code for the Sum and Prod ares nearly identical except for the fact that the accept methods in those classes respectively invoke the forSum and forProd, methods of the Visitor argument v. They all have Object as their return type.


next up previous
Next: 1.12.4 Polymorphic Visitors with Up: 1.12 The Visitor Pattern Previous: 1.12.2 Openness in Data
Corky Cartwright
2000-01-07