说到访问者模式就不得不提一下双分派的问题,什么是双分派呢,我们先来解释一下什么是单分派和多分派。
根据对象的类型而对方法进行的选择,就是分派(Dispatch)
分派的类型
一个方法所属的对象叫做方法的接收者,方法的接收者与方法的参量统称做方法的宗量。
根据分派可以基于多少种宗量,可以将面向对象的语言划分为单分派语言和多分派语言。单元分派语言根据一个宗量的类型(真实类型)进行对方法的选择,多分派语言根据多于一个的宗量的类型对方法进行选择。
C++和Java以及Smaltalk都是单分派语言;多分派语言的例子包括CLOS和Cecil。按照这样的区分,C++和Java就是动态的单分派语言,因为这两种语言的动态分派仅仅会考虑到方法的接收者的类型,同时又是静态的多分派语言,因为这两种语言对重载方法的分派会考虑到方法的接收者的类型和方法所有参量的类型。
根据分派发生的时期,可以将分派分为两种,即分派分
静态分派和动态分派。
静态分派(Static Dispatch) 发生在编译时期,分派根据静态类型信息发生。方法重载(Overload)就是静态分派。(所谓的:编译时多态)
动态分派(Dynamic Dispatch) 发生在运行时期,动态分派动态地置换掉某个方法。面向对象的语言利用动态分派来实现方法置换产生的多态性。(所谓的:运行时多态)
单分派语言处理一个操作是根据请求者的名称和收到的参数决定的,在Java中有静态绑定和动态绑定直说,它的实现是依据重载(overload)和覆写(override)实现的
举个简单的例子,演员可以演电影角色,一个演员可以扮演多个角色
public interface Role {
//演员要扮演的角色
}
public class KunFuRole implements Role {
//扮演一个武功高强的角色
}
角色有了,然后我们需要定义演员
public abstract class AbsActor {
//演员能扮演角色
public void act(Role role){
System.out.println("演员能扮演任何角色");
}
//只演功夫戏的角色
public void act(KunFuRole role){
System.out.println("这个演员只演功夫戏");
}
}
public class YoungActor extends AbsActor {
@Override
public void act(KunFuRole role){
System.err.println("年轻演员喜欢演功夫戏");
}
}
public class OldActor extends AbsActor {
//不演功夫角色
@Override
public void act(KunFuRole role) {
System.err.println("太老了,演不动了");
}
}
此时覆写和重载都实现了
我们查看场景类
public class Main {
public static void main(String[] args) {
//定义一个演员
AbsActor absActor = new OldActor();
//定义一个角色
Role role = new KunFuRole();
//开始演戏
absActor.act(role);
absActor.act(new KunFuRole());
}
}
结果
重载在编译器时期就决定了要调用哪个方法,它是根据role的表面类型而决定调用act(Role role)方法,这是静态绑定;而Actor的执行方法act则是由其实际类型决定的。这是动态绑定,
一个演员可以扮演多个角色,系统要适应这种变化,也就是根据演员和角色两个对象类型完成不同的操作任务,此时引入访问者模式就可以了
public interface Role {
//演员要扮演的角色
public void accpet(AbsActor absActor);
}
public class KunFuRole implements Role {
//扮演一个武功高强的角色
@Override
public void accpet(AbsActor absActor) {
absActor.act(this);
}
}
public class IdiotRole implements Role{
//扮演一个弱智角色
@Override
public void accpet(AbsActor absActor) {
absActor.act(this);
}
}
public class Main {
public static void main(String[] args) {
//定义一个老演员
AbsActor oldActor = new OldActor();
//定义一个年轻演员
AbsActor youngActor = new YoungActor();
//定义一个角色
Role role = new KunFuRole();
role.accpet(oldActor);
role.accpet(youngActor);
}
}
这样无论演员类如何变化,我们都能找到期待的方法运行,这就是双分派
双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型,它是双分派的一个特例,这里也可以看出java是一个支持双分派的单分派语言
ps:Java不支持动态的多分派。但可以通过使用设计模式,在Java语言里面实现动态的双重分派(ps:就是“伪双重分派”是由两次的单分派组成)
JAVA语言支持静态的多分派和动态的单分派