让我最后罗嗦一下Visitor模式——关于双重分派
在昨天的Post中我提到了Visitor模式中最重要的一点是:double-dispatch(双重分派)。说来惭愧,昨天在写post的时候,我仅仅在震宇兄的Post中对double-dispatch有了概念上的理解,并且认为accept方法是为了实现double-dispatch的。然而idior和Cavingdeep兄的问题让我心里没了谱:accept为什么要存在,为什么不直接visit呢?早上在看了Cavingdeep兄的Post之后,我想要解决这个问题,得从到底什么是双重分派入手了。于是google了一番,终于有点开窍了。
在说双重分派以前,我们先来看看以下代码[1]:
SuperClass a = new SubA();
SuperClass parameter = new SubB();
a.commonMethod(parameter);
我们都知道由于动态类型绑定,尽管a声明为SuperClass类型,但a.commonMethod调用的是SubA中override的commonMethod,这就是多态的一种体现。如果在SubA类中有这样的重载方法:
commonMethod(SuperClass arg) {
}
commonMethod(SubA arg) {
}
commonMethod(SubB arg) {
}那么a.commonMethod调用的又是哪个方法呢?你的心里有非常肯定的答案吗?写段代码尝试一下,你会发现第一个commonMethod被调用了。这里就引出了一个问题:动态类型绑定只会体现在方法的调用者身上,而方法的参数类型则会在编译期由编译器决定。
如果参数类型也能够在运行期决定,那么哪个commonMethod被调用就由方法调用者和方法参数共同在运行期决定了。那么如何实现参数类型在运行期绑定呢?既然方法调用者的类型是运行期才确定的,那么我们就可以反客为主了,将方法参数变成方法调用者。
commonMethod(SuperClass arg) {
arg.commonMethod(this);
}至此,您应该明白双重分派的涵义了吧?哪个commonMethod最终被调用经过两次运行期类型绑定才确定下来,这样的过程就是双重分派了。由此延伸开来,多重分派也就不难理解了。
在双重分派当中,还有一个关键细节,您是否发现了呢?那就是您所期待的SubA中的CommonMethod(SubB arg)方法被调用的情况并没有出现,反而是SubB中的CommonMethod(SubA arg)的方法被调用了。不管怎么样,双重分派给程序带来了更多的灵活性。
说完了双重分派,我们再回到Visitor模式上来。Visitor模式其实就是将以上所说的commonMethod从数据对象中抽离出来封装至单独的类中,那么为了维持原有的双重分派,accept方法就派上了用场。没有了accept,就无法在数据操作从数据对象中分离出来的同时保持双重分派。同时,您也会发现commonMethod有一个非常大的特点:它是一个跨对象操作的方法。对应到Visitor模式里众多的Visitor类,重载的visit方法正好体现了跨对象操作的特点。
也许我讲述的还不是很清楚,有兴趣的朋友可以参考以下这篇给我带来很大启发的文章。
[1] Double Dispatch Mechanism
Feedback
AVisitor: Visitor
BVisitor: Visitor
AElement: Element
BElement: Element
AVisitor aV=new AVisitor();
AElement aE=new AElement();
aV.Visit(aE);
bV.Visit(bE);
# re: 让我最后罗嗦一下Visitor模式——关于双重分派
2005-08-20 22:52 by FantasySoftVisitor aV=getVisitor(); // getVisitor方法返回Visitor的子类实例
Element aE = getElement(); // getElement方法返回Element的子类实例
aV.Visit(aE);
呵呵 我没表达好, 其实就是你这个意思.
但是这个时候并没有用accept啊?
---
早上在看了Cavingdeep兄的Post之后,
---
在哪? 我很想看看他的想法
# re: 让我最后罗嗦一下Visitor模式——关于双重分派
2005-08-21 00:35 by FantasySoft至于Cavingdeep的Post,你得找他要了。
# re: 让我最后罗嗦一下Visitor模式——关于双重分派
2005-08-21 07:56 by Cavingdeep就是因为上面FantasySoft提出的问题,所以我删掉了那篇blog,按照那样的写法,必须在Element方法中作出类型判断,但是这样不能做到静态类型检查,没有Accept方法的优势。

浙公网安备 33010602011771号