mondrian 源码解读(五)-访问者模式

在上一节中,我们看到在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()方法来访问各种元素。