访问者模式二
双分派
孤陋寡闻了,没听过过这名词。
public interface Role {} // 角色
public class KungFuRole implements Role {} // 功夫角色
public abstract class AbsActor { //演员
// 重载了方法
public void act(Role role){
System.out.println("演员可以扮演任何角色");
}
public void act(KungFuRole role){
System.out.println("演员都可以演功夫角色");
}
}
public class OldActor extends AbsActor { //不演功夫角色
public void act(KungFuRole role){
System.out.println("年龄大了,不能演功夫角色");
}
}
public class Client {
public static void main(String[] args) {
AbsActor actor = new OldActor();
Role role = new KungFuRole();
actor.act(role);
}
}
最后的结果是输出“演员可以扮演任何角色”,但是想要的是"年龄大了,不能演功夫角色",面向接口,在依赖倒置那说过这,应该写成 Role role = new KungFuRole()
,但是 AbsActor 是重载了两个方法,其中参数还是有父子关系的,它会匹配到 Role 参数的方法,所以无法得到想要的结果。
改造角色接受演员,演员作为角色的访问者。
public interface Role {
public void accept(AbsActor actor);
}
public class KungFuRole implements Role {
public void accept(AbsActor actor){
actor.act(this);
}
}
public class Client {
public static void main(String[] args) {
AbsActor actor = new OldActor();
Role role = new KungFuRole();
role.accept(actor);
}
}
由 Role 内部接受演员,然后自动调用演员的 act 方法。调用 role 的方法,用的是具体实现 KungFuRole 的 accept,内部调用 actor 的 act 方法,这个方法的参数 this 是 KungFuRole,所以能执行到正确的结果。
不管演员类和角色类怎么变化,我们都能够找到期望的方法运行,这就是双分派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。
没听得懂。看了多分派_百度百科:
变量被声明时的类型叫做变量的静态类型,或叫做明显类型,而变量所引用的对象的真实类型叫做实际类型,这种根据对象的类型而对方法进行的选择,就是分派。
静态分派发生在编译时期,分派根据静态类型信息发生。方法重载就是静态分派。
简单来说,单分派是指调用一个对象的方法仅由对象的类型决定。多分派是指调用一个对象的方法不仅由对象的类型决定,同时还由其他因素决定,比如方法参数的类型等等。
又查了些资料,Java 是单分派的,执行哪个对象的哪个方法,只跟对象的运行时类型有关,跟参数的运行时类型无关。actor 运行时类型是 OldActor,参数运行时状态是 KungFuRole,但编译时类型是 Role,它不用参数的运行时类型,所以输出是错误的"演员可以扮演任何角色"。
AbsActor actor = new OldActor();
Role role = new KungFuRole();
actor.act(role);
如果 Java 支持双分派,执行时还看参数的运行时类型,那么结果就正确了,就不需要将 AbsActor 改造成 Role 的访问者了,这么弄好像有点明白了。
AbsActor actor = new OldActor();
Role role = new KungFuRole();
role.accept(actor);
role 会使用它的运行时状态 KungFuRole,然后 actor 作为参数传进去,这方法没什么重载,然后再将参数作为对象,调用它自己的方法,这么一来,还是使用传进去的参数的运行时类型 OldActor。所以作为一个整体来说,Role 和 AbsActor 都需要用运行时类型,就实现了双分派。