`
javatar
  • 浏览: 1704662 次
  • 性别: Icon_minigender_1
  • 来自: 杭州699号
社区版块
存档分类
最新评论

CommonTemplate访问者设计思考

    博客分类:
  • HTTL
阅读更多
经过多个版本的调整, CommonTemplate(http://www.commontemplate.org)的核心包设计逐渐稳定.
但访问者的设计一直是块心病, 并且访问者是合成模式[GoF95]树结构中比较重要的扩展点.
CommonTemplate中的访问者最开始设计:
public interface Visitor {

	/**
	 * 当访问到节点时被回调
	 * @param node 被访问的节点
	 */
	void visit(Node node);

}

其中, Node是Template, Element, Expression等的抽象. 如下:
public interface Node {

	/**
	 * 接收访问者, 并带领访问者遍历整个子树. (前序遍历)
	 * @param visitor 访问者
	 */
	void accept(Visitor visitor);


	String getName();

	......

}

Node是引擎实现的, 而Visitor是留给扩展者实现的.
大体分为:
1. 模板元素树和表达式树全遍历
2. 模板元素树全遍历(不访问表达式树)
3. 查找某一模板元素
4. 查找某一表达式元素(基于模板遍历)
如:
NodeCountVisitor (统计模板节点的个数)
TemplateDumpVisitor (导出模板结构)
DirectiveFindVisitor (查找指令)
VariableRequirementVisitor (计算模板所需的变量)
等等.
调用方式如:
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);

}

这样, 可以用 if (node instanceof Expression) return SKIP; 控制不访问表达式树.
也可以通过return STOP; 停止访问.
当然, Node中的int accept(Visitor)方法也要返回和传递控制值:
public interface Node {

	/**
	 * 接收访问者, 并带领访问者遍历整个子树. (前序遍历)
	 * @param visitor 访问者
	 * @return 访问控制值, Visitor.NEXT, Visitor.SKIP, Visitor.STOP
	 */
	int accept(Visitor visitor);

}

然而, Visitor接口中单一的visit()方法强迫扩展者使用if(node instanceof XXX)语句判断类型, 丧失多态性.
重构:
将visit拆分, 依赖树的具体结点.
public abstract class Visitor { // 考虑树的具体结点可能增加, 采用抽象类便于向前兼容

	public int visitDirective(Directive directive){}

	public int visitVariable(Variable variable){}

	......

	或者:

	public int visit(Directive directive){}

	public int visit(Variable variable){}

	......

}

这样, 子类只要覆写需要的类型函数.
但过多的状态位控制流转, 也是一件不愉快的事.
并且表达式树与模板元素树两种类型没有区分.
重构:
拆分表达式树与模板元素树访问者,
并通过子类覆写的方式决定是否需要级联访问表达式树,
通过抛出StopVisitException运行时异常停止访问.
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 {}

}

节点的accept也作相应处理:
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);

}
2
0
分享到:
评论
1 楼 yananay 2008-10-16  
  

相关推荐

    commontemplate框架

    **共通模板(CommonTemplate)框架详解** 共通模板(CommonTemplate)框架是一个基于Java的JSP模板化工具,旨在简化Web应用中的视图层开发,提高开发效率和代码复用性。它允许开发者将HTML代码与Java代码分离,使得...

    commontemplate-0.8.1.jar

    commontemplate-0.8.1.jar,是使用jwebap所需要的jar包。没有它,启动会有错误

    CommonTemplate进入中国开源软件竞赛决赛

    在"CommonTemplate Engine.ppt"这个文件中,很可能是项目团队对CommonTemplate的详细介绍,包括设计理念、功能特性、使用方法以及示例应用等内容。通过阅读这份演示文稿,我们可以更深入地理解CommonTemplate的工作...

    动软分享社区模板

    - **如何在主模板中调用共通模板文件**:可以通过`@Html.Partial("_CommonTemplate.cshtml")`的方式在主模板中引入共通模板文件。 - **如何更改模板中的缩略图尺寸**:通常需要在模板文件中设置图片大小属性,例如...

    zencart模板系统文件笔记2:tpl_header.php.pdf

    - **其他导航元素**:根据模板设计,可能还包括其他导航元素,如购物车图标、客服联系方式等。 #### 文件结构与实现细节 在`tpl_header.php`文件开头,有一段注释说明了该文件的作用及如何进行覆盖定制。下面将...

Global site tag (gtag.js) - Google Analytics