The visitor pattern, even when simplified using multimethods, may not be the most direct approach to adding operations (such as evaluation and conversion) to classes (such as those for sums, products, and constants).
Wouldn't it be nice to stick with the simple idea that such operations are methods of the classes? Recall that in the basic object-oriented composite pattern, we had classes within the Expr class hierarchy that had methods within them such as the following:
public int evaluate(){ return left.evaluate() + right.evaluate(); }
The idea of open classes is to stick with simple methods
such as this, but to allow all the various evaluate
methods to be packaged together in a single compilation unit which can
be added on separately, rather than dispersing the methods within the
code of the classes.
In MultiJava, this can be achieved by having, for example, an
Evaluation.java
file that contains the following four method
definitions, each of which augments a different class:
public int Expr.evaluate(){ throw new Error("Unknown type of Expr: " + this); } public int Constant.evaluate(){ return getValue(); } public int Sum.evaluate(){ return getLeft().evaluate() + getRight().evaluate(); } public int Product.evaluate(){ return getLeft().evaluate() * getRight().evaluate(); }
One interesting note is that these externally introduced methods
need to use the public accessor methods such as getValue
and getLeft
rather than having direct access to the
private instance variables such as value
and
left
. That is, the private/public distinction is defined
in terms of compilation units, not classes. A method introduced into
a class from a separate compilation unit does not have access to
private members of the class.
Essentially the same approach can be used in AspectJ, where the introduction of methods to open classes is treated as a special case of the broader notion of Aspect Oriented Programming, a topic we will discuss at the end of the semester. In AspectJ, the three method definitions would be written as shown above, but wrapped in an aspect definition. Additionally, rather than the Expr class needing a concrete method that throws an Error, the method can be abstract:
public aspect Evaluation { public abstract int Expr.evaluate(); public int Constant.evaluate(){ return getValue(); } public int Sum.evaluate(){ return getLeft().evaluate() + getRight().evaluate(); } public int Product.evaluate(){ return getLeft().evaluate() * getRight().evaluate(); } }
I haven't been able to get the MultiJava compiler to work on my system yet, but I can definitely demo this approach in class using AspectJ. We could then either look at adding an additional aspect or additional type of AST node using this approach, or using the Visitor pattern in standard Java, or some mixture.