Case Study of Writing the Extensible Visitor Pattern
From APIDesign
Have You Ever Wondered...?
Do you wonder whether knowledge of proper design patterns makes you good API designer? Yes and no. Of course knowing regular design patterns simplifies communication, understanding, etc. However there are hidden catches. Not every design pattern belongs among APIDesignPatterns - it may not be ready for evolution. For example the well known Visitor pattern is really not evolvable easily as analysed by Chapter 18.
Tripple Dispatch
The Chapter 18 discusses various approaches to implement visitor in an evolvable API. The learning path itself is important, but to stress the important point, here is the code for the final solution of the expression problem:
Code from Language.java:
See the whole file.public interface Expression { public abstract void visit(Visitor v); } public interface Plus extends Expression { public Expression getFirst(); public Expression getSecond(); } public interface Number extends Expression { public int getValue(); } public static abstract class Visitor { Visitor() {} public static Visitor create(Version10 v) { return create10(v); } public interface Version10 { public boolean visitUnknown(Expression e); public void visitPlus(Plus s); public void visitNumber(Number n); } public abstract void dispatchPlus(Plus p); public abstract void dispatchNumber(Number n); }
Visitors written using this style are easily extensible, by adding new expression types as shown in version 2.0:
Code from Language.java:
See the whole file./** @since 2.0 */ public interface Minus extends Expression { public Expression getFirst(); public Expression getSecond(); } public static abstract class Visitor { Visitor() {} /** @since 2.0 */ public static Visitor create(Version20 v) { return create20(v); } /** @since 2.0 */ public interface Version20 extends Version10 { public void visitMinus(Minus m); } /** @since 2.0 */ public abstract void dispatchNumber(Number n); }
This is the most flexible solution. It uses form of tripple dispatch - e.g. the final visit method is defined by the expression type, version of the expression language and implementation of the visitor interface:
Code from Language.java:
See the whole file.static Visitor create20(final Visitor.Version20 v) { return new Visitor() { @Override public void dispatchPlus(Plus p) { v.visitPlus(p); } @Override public void dispatchNumber(Number n) { v.visitNumber(n); } @Override public void dispatchMinus(Minus m) { v.visitMinus(m); } @Override public void dispatchReal(Real r) { v.visitUnknown(r); } }; }
This solution to the expression problem is another realization of the general principle to separate ClientAPI from ProviderAPI.