在上一节中,我们看到在resolve()方法中使用了大量的访问者模式,所以下面我们总结下:
什么是访问者模式
我发现还是GOF书中定义的最好,就拿过来了。
意图
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提
下定义作用于这些元素的新操作。
结构图
动机
访问者可以用在什么地方? 在这种地方你一定要考虑到使用访问者模式:业务规则要求遍历多个不同的对象。
适用性
在下列情况下使用 Vi s i t o r模式:
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于
其具体类的操作。 - 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操
作“污染”这些对象的类。 Vi s i t o r使得你可以将相关的操作集中起来定义在一个类中。
当该对象结构被很多应用共享时,用 Vi s i t o r模式让每个应用仅包含需要用到的操作。 - 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需
要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那
么可能还是在这些类中定义这些操作较好。
参与者
- Vi s i t o r(访问者,如 N o d e Vi s i t o r)
— 为该对象结构中 C o n c r e t e E l e m e n t的每一个类声明一个 Vi s i t操作。该操作的名字和特
征标识了发送 Vi s i t请求给该访问者的那个类。这使得访问者可以确定正被访问元素
的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。 - C o n c r e t e Vi s i t o r(具体访问者,如 Ty p e C h e c k i n g Vi s i t o r)
— 实现每个由 Vi s i t o r声明的操作。每个操作实现本算法的一部分,而该算法片断乃是
对应于结构中对象的类。 C o n c r e t e Vi s i t o r为该算法提供了上下文并存储它的局部状态。
这一状态常常在遍历该结构的过程中累积结果。 - E l e m e n t(元素,如 N o d e)
— 定义一个 A c c e p t操作,它以一个访问者为参数。 - C o n c r e t e E l e m e n t(具体元素,如 A s s i g n m e n t N o d e, Va r i a b l e R e f N o d e)
— 实现A c c e p t操作,该操作以一个访问者为参数。 - O b j e c t S t r u c t u r e(对象结构,如 P r o g r a m)
— 能枚举它的元素。
— 可以提供一个高层的接口以允许该访问者访问它的元素。
— 可以是一个复合(参见 C o m p o s i t e( 4 . 3))或是一个集合,如一个列表或一个无序集
合。
协作
- 一个使用 Vi s i t o r模式的客户必须创建一个 C o n c r e t e Vi s i t o r对象,然后遍历该对象结构,
并用该访问者访问每一个元素。 - 当一个元素被访问时,它调用对应于它的类的 Vi s i t o r操作。如果必要,该元素将自身作
为这个操作的一个参数以便该访问者访问它的状态。
下面的交互框图说明了一个对象结构、一个访问者和两个元素之间的协作。
优缺点
- 访问者模式使得易于增加新的操作
- 访问者集中相关的操作而分离无关的操作
- 增加新的 C o n c r e t e E l e m e n t类很困难
- 通过类层次进行访问
- 累积状态
- 破坏封装
写了这么多,其实都是GOF书上的,建议直接看书,理解的更透彻。
mondrian对访问者模式的应用
exp接口
在exp中,有3个方法,分别是Exp accept(Validator validator);
Calc accept(ExpCompiler compiler);
Object accept(MdxVisitor visitor);
,所以Exp就是Element。因为mdx的语法是基本固定的,并且可能这个接口中的许多对象进行不同的操作,所以在这里使用visitor模式再好不过了。
/**
* An <code>Exp</code> is an MDX expression.
*
* @author jhyde, 20 January, 1999
* @since 1.0
*/
public interface Exp {
Exp clone();
/**
* Returns the {@link Category} of the expression.
*
* @post Category.instance().isValid(return)
*/
int getCategory();
/**
* Returns the type of this expression. Never null.
*/
Type getType();
/**
* Writes the MDX representation of this expression to a print writer.
* Sub-expressions are invoked recursively.
*
* @param pw PrintWriter
*/
void unparse(PrintWriter pw);
/**
* Validates this expression.
*
* The validator acts in the role of 'visitor' (see Gang of Four
* 'visitor pattern'), and an expression in the role of 'visitee'.
*
* @param validator Validator contains validation context
*
* @return The validated expression; often but not always the same as
* this expression
*/
Exp accept(Validator validator);
/**
* Converts this expression into an a tree of expressions which can be
* efficiently evaluated.
*
* @param compiler
* @return A compiled expression
*/
Calc accept(ExpCompiler compiler);
/**
* Accepts a visitor to this Exp.
* The implementation should generally dispatches to the
* {@link MdxVisitor#visit} method appropriate to the type of expression.
*
* @param visitor Visitor
*/
Object accept(MdxVisitor visitor);
}
Exp的继承层次图
Validator
validator对不同的表达式进行不同的校验。
public interface Validator {
/**
* Returns the {@link Query} which is being validated.
*/
Query getQuery();
/**
* Validates an expression, and returns the expression it resolves to.
*
* @param exp Expression to validate
* @param scalar Whether the context requires that the expression is
* evaluated to a value, as opposed to a tuple
*/
Exp validate(Exp exp, boolean scalar);
/**
* Validates a usage of a parameter.
*
* <p>It must resolve to the same object (although sub-objects may change).
*/
void validate(ParameterExpr parameterExpr);
/**
* Validates a child member property.
*
* <p>It must resolve to the same object (although sub-objects may change).
*/
void validate(MemberProperty memberProperty);
/**
* Validates an axis.
*
* It must resolve to the same object (although sub-objects may change).
*/
void validate(QueryAxis axis);
/**
* Validates a formula.
*
* It must resolve to the same object (although sub-objects may change).
*/
void validate(Formula formula);
}
ExpCompiler
MdxVisitor
Query
Query就是一个对象结构,即ObjectStructure。通过调用自身的resolve()方法来访问各种元素。