CommonTemplate访问者设计思考
经过多个版本的调整, CommonTemplate(http://www.commontemplate.org)的核心包设计逐渐稳定.
但访问者的设计一直是块心病, 并且访问者是合成模式[GoF95]树结构中比较重要的扩展点.
CommonTemplate中的访问者最开始设计:
public interface Visitor {/** * 当访问到节点时被回调 * @param node 被访问的节点 */void visit(Node node);}
public interface Node {/** * 接收访问者, 并带领访问者遍历整个子树. (前序遍历) * @param visitor 访问者 */void accept(Visitor visitor);String getName();......}
Visitor visitor = new TemplateDumpVisitor(writer);template.accept(visitor); // 带领visitor遍历整个树, 遇到节点则回调visitor的相应方法
public interface Visitor {/** * 继续访问下一节点 */public static final int NEXT = 0;/** * 跳过子节点 */public static final int SKIP = 1;/** * 停止访问 */public static final int STOP = 2;/** * 当访问到节点时被回调 * @param node 被访问的节点 * @return 访问控制值, NEXT, SKIP, STOP */int visit(Node node);}
public interface Node {/** * 接收访问者, 并带领访问者遍历整个子树. (前序遍历) * @param visitor 访问者 * @return 访问控制值, Visitor.NEXT, Visitor.SKIP, Visitor.STOP */int accept(Visitor visitor);}
public abstract class Visitor { // 考虑树的具体结点可能增加, 采用抽象类便于向前兼容public int visitDirective(Directive directive){}public int visitVariable(Variable variable){}......或者:public int visit(Directive directive){}public int visit(Variable variable){}......}
public abstract class ExpressionVisitor {/** * 当访问到变量时被回调 * * @param variable 访问到的变量 * @throws StopVisitException 当希望停止访问时抛出 */public void visitVariable(Variable variable) throws StopVisitException {}/** * 当访问到常量时被回调 * * @param constant 访问到的常量 * @throws StopVisitException 当希望停止访问时抛出 */public void visitConstant(Constant constant) throws StopVisitException {}/** * 当访问到二元操作符时被回调 * * @param binaryOperator 访问到的二元操作符 * @throws StopVisitException 当希望停止访问时抛出 */public void visitBinaryOperator(BinaryOperator binaryOperator) throws StopVisitException {}/** * 当访问到一元操作符时被回调 * * @param unaryOperator 访问到的一元操作符 * @throws StopVisitException 当希望停止访问时抛出 */public void visitUnaryOperator(UnaryOperator unaryOperator) throws StopVisitException {}}
public abstract class TemplateVisitor extends ExpressionVisitor {/** * 当访问到模板时被回调 * * @param template 访问到的模板 * @throws StopVisitException 当希望停止访问时抛出 */public void visitTemplate(Template template) throws StopVisitException {}/** * 模板访问结束时被回调 * * @param template 结束的模板 * @throws StopVisitException 当希望停止访问时抛出 */public void endTemplate(Template template) throws StopVisitException {}/** * 当访问到文本块或不解析块时被回调 * * @param text 访问到的文本块或不解析块 * @throws StopVisitException 当希望停止访问时抛出 */public void visitText(Text text) throws StopVisitException {}/** * 当访问到行注释或块注释时被回调 * * @param comment 访问到的行注释或块注释 * @throws StopVisitException 当希望停止访问时抛出 */public void visitComment(Comment comment) throws StopVisitException {}/** * 当访问到行指令时被回调.<br> * 注:缺省实现为继续访问指令表达式。<br> * 如果不需要访问指令表达式,请覆写此函数并留空。<br> * 也可以在访问指令表达式前后作相关处理:<br> * <pre> * public void visitDirective(Directive directive) { * // 在表达式访问之前处理... * super.visitDirective(directive); * // 在表达式访问之后处理... * } * </pre> * @param directive 访问到的行指令 * @throws StopVisitException 当希望停止访问时抛出 */public void visitDirective(Directive directive) throws StopVisitException {if (directive.getExpression() != null)directive.getExpression().accept(this);}/** * 当访问到块指令时被回调.<br> * 注:缺省实现为继续访问指令表达式。<br> * 如果不需要访问指令表达式,请覆写此函数并留空。<br> * 也可以在访问指令表达式前后作相关处理:<br> * <pre> * public void visitBlockDirective(BlockDirective blockDirective) { * // 在表达式访问之前处理... * super.visitBlockDirective(blockDirective); * // 在表达式访问之后处理... * } * </pre> * @param blockDirective 访问到的块指令 * @throws StopVisitException 当希望停止访问时抛出 */public void visitBlockDirective(BlockDirective blockDirective) throws StopVisitException {if (blockDirective.getExpression() != null)blockDirective.getExpression().accept(this);}/** * 块指令访问结束时被回调 * * @param blockDirective 结束的块指令 * @throws StopVisitException 当希望停止访问时抛出 */public void endBlockDirective(BlockDirective blockDirective) throws StopVisitException {}}
public abstract class Node {/** * 接收访问者, 并带领访问者遍历整个子树. (前序遍历) * @param visitor 访问者 */void accept(Visitor visitor) {accept(visitor, true);}/** * 状态传入访问者接收接口, 通常直接使用accept(Visitor visitor) * @param visitor 访问者 * @param isEnter 是否为入口, 在入口处忽略StopVisitException */void accept(Visitor visitor, boolean isEnter);}