行为型-访问者模式
# 访问者模式
- Visitor Design Pattern:Allows for one or more operation to be applied to a set of objects at runtime, decoupling the operations from the object structure.
- 允许一个或多个操作应用到一组对象上,解耦操作和对象本身。
- 访问者模式针对的是一组类型不同的对象(PDFFile,PPTFile,WordFile),尽管这组对象的类型不同,但是他们继承相同的父类(ResourceFile)或者实现相同的接口,在不同的应用场景下,我们需要对这组对象进行一系列不相关的业务操作(抽取、压缩),但为了避免不断增加功能导致的频繁代码改动,我们使用访问者模式,将对象与操作解耦,将这些业务操作抽离出来,定义在独立细分的访问者类中。
- 访问者模式的关键是巧用重载函数,但是函数重载在大部分面向对象编程语言中是静态绑定的,调用类的那个重载函数,在编译器由参数的声明类型决定的,而非运行时,根据参数的实际类型决定的。
- 访问者模式解决的痛点主要是需要动态绑定的类型,所以调用那个重载版本,其参数中的子类必须传入静态类型为目标子类的参数,并在方法中使用传入参数的动态绑定。
# 为什么支持双分派的语言不需要访问者模式?
- 单分派(Single Dispatch):根据对象的运行时类型来决定执行哪个对象的方法;根据方法参数的编译时类型来决定执行对象的那个方法。
- 双分派(Double Dispatch):根据对象的运行时类型来决定执行哪个对象的方法,;根据方法参数的运行时类型来决定执行对象的哪个方法。
- Single Dispatch 之所以称为“Single”,是因为执行哪个对象的哪个方法,只跟“对象”的运行时类型有关。Double Dispatch 之所以称为“Double”,是因为执行哪个对象的哪个方法,跟“对象”和“方法参数”两者的运行时类型有关。
- Single Dispatch 和 Double Dispatch 跟多态和函数重载直接相关。当前主流的面向对象编程语言(比如,Java、C++、C#)都只支持 Single Dispatch,不支持 Double Dispatch。
- Java 支持多态特性,代码可以在运行时获得对象的实际类型(也就是前面提到的运行时类型),然后根据实际类型决定调用哪个方法。
- 尽管 Java 支持函数重载,但 Java 设计的函数重载的语法规则是,并不是在运行时,根据传递进函数的参数的实际类型,来决定调用哪个重载函数,而是在编译时,根据传递进函数的参数的声明类型(也就是前面提到的编译时类型),来决定调用哪个重载函数。也就是说,具体执行哪个对象的哪个方法,只跟对象的运行时类型有关,跟参数的运行时类型无关。所以,Java 语言只支持 Single Dispatch。
- 对于资源文件处理工具这个例子,如果工具提供的功能并不多,知识几个而已,那我们推荐使用工厂模式的实现方式,毕竟代码更加清晰、易懂。如果工具提供非常多的功能,比如有十几个,那我更推荐顺颂访问者模式,因为访问者模式需要定义的类要比工厂模式的实现方式少很多,类太多也会影响到代码的可维护性。
- 一个对象调用另一个对象的方法,就相当于给它发送一条消息,这条消息起码要包含对象名、方法名和方法参数。
代码执行结果
public class ParentClass {
public void f() {
System.out.println("I am ParentClass's f().");
}
}
public class ChildClass extends ParentClass {
public void f() {
System.out.println("I am ChildClass's f().");
}
}
public class SingleDispatchClass {
public void polymorphismFunction(ParentClass p) {
p.f();
}
public void overloadFunction(ParentClass p) {
System.out.println("I am overloadFunction(ParentClass p).");
}
public void overloadFunction(ChildClass c) {
System.out.println("I am overloadFunction(ChildClass c).");
}
}
public class DemoMain {
public static void main(String[] args) {
SingleDispatchClass demo = new SingleDispatchClass();
ParentClass p = new ChildClass();
demo.polymorphismFunction(p);//执行哪个对象的方法,由对象的实际类型决定
demo.overloadFunction(p);//执行对象的哪个方法,由参数对象的声明类型决定
}
}
//代码执行结果:
I am ChildClass's f().
I am overloadFunction(ParentClass p).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
上次更新: 2020/11/29, 15:11:00