面向对象语言的多分派、单分派、双重分派

来源:互联网 发布:股市时间窗口算法 编辑:程序博客网 时间:2024/06/05 04:16
变量被声明时的引用类型是变量的静态类型,真实类型是变量的实际类型。根据变量的类型进行方法的选择就是分派。在编译阶段产生静态分派,根据静态类型发生,比如方法重载,发生在运行阶段的是动态分派,动态置换调用的方法以此做到多态。
方法所属的类型是方法的接受者,而方法的接受者和方法的参量就是所谓的宗量。根据一个宗量选择方法就是单分派,比如java运行期选择方法是根据方法的类型,根据多个宗量选择方法是多分派比如方法重载。多分派是由一系列多分派组成的过程。java是静态的多分派和动态的单分派语言。那么(1)可以通过在参量上传入不同类型的方式使方法的调用依赖多个宗量来进行选择,但是增加逻辑判断,代码冗余而且不易扩展(2)使用访问者模式实现参量的往来传递,进而达到使用双分派的过程。两个等级结构上的数据节点向访问者对象传入节点对象,而访问这对象反过来执行数据节点上的操作。

多分派机制的问题是效率低下,逻辑复杂。



在说双重分派以前,我们先来看看以下代码[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方法正好体现了跨对象操作的特点。

0 0
原创粉丝点击