如果像是Java这样的静态代码支持了双委派机制,那么就不再需要我们的访问者模式了

,至于为什么不需要了,需要我们先从双委派机制说起

首先说 Doule Dispatch,就是翻译为双委派机制,这个机制对应着,还有SingleDispatch,单分派值得是执行哪个对象的方法,根据的是对象运行时的类型决定的,执行对象的哪个方法,是根据对象参数的编译时的类型确定的,所谓Double Dispatch,执行哪个对象的方法,根据对象的运行时类型决定,执行对象的哪个方法,可以根据对象参数的运行时类型来决定.

对于其中的Dispatch单词,可以理解为是一种消息的传递,也就是Dispatch,一个对象调用另一个对象的方法,相当于给其发送一个消息,包含对象名,方法名,参数

那么,如何理解Single,Double的单词,single是指执行哪个对象的哪个方法,只跟对象的运行时类型有关,Double则是执行哪个对象的哪个方法,跟对象和方法参数两者的运行时类型有关,现在主流的面向对象编程语言,Java C++ C# 都只是支持Single Dispatch,不支持Double Dispatch

Java中极其的明显,支持多态的特性,可以在运行时获取到对象的实际类型,根据实际类型调用哪个方法,但是对于函数的重载,在一个类中多个函数,则是在编译时期,根据传递的函数的声明类型来决定调用哪个重载函数,具体执行哪个对象的哪个方法,只是和对象的运行时的类型有关,和参数的运行时的类型无关

下面的代码解释了什么是单委派机制

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).

上面的代码中,的polymorphismFunction()函数,执行的是p的是实际类型的f函数,而在overloadFunction()函数中,匹配的是重载函数中的overloadFunction(ParentClass p)根据p的类型来决定匹配哪个重载函数

假设Java支持Double Dispatch,那上节代码中resourceFile就可以获取到实际类型,直接运行了,也就不需要访问者模式了

这也就是为啥有了Double Dispatch就不再需要访问者模式了

当然对于上面的需求,除了访问者模式,还有别的实现方案吗?

我们可以使用一个工厂模式来进行实现,定义一个extract2txt()接口函数的Extractor接口,然后分别实现PdfExtractor,PPTExtractor,WordExtractor三个实现类,然后让ExtractorFactory工厂类中,分别根据不同的File类型,返回不同的Extractor处理器

实现起来并不困难

public abstract class ResourceFile {

protected String filePath;

public ResourceFile(String filePath) {

this.filePath = filePath;

}

public abstract ResourceFileType getType();

}

public class PdfFile extends ResourceFile {

public PdfFile(String filePath) {

super(filePath);

}

@Override

public ResourceFileType getType() {

return ResourceFileType.PDF;

}

//…

}

//…PPTFile/WordFile跟PdfFile代码结构类似,此处省略…

public interface Extractor {

void extract2txt(ResourceFile resourceFile);

}

public class PdfExtractor implements Extractor {

@Override

public void extract2txt(ResourceFile resourceFile) {

//…

}

}

//…PPTExtractor/WordExtractor跟PdfExtractor代码结构类似,此处省略…

public class ExtractorFactory {

private static final Map<ResourceFileType, Extractor> extractors = new HashMap<>();

static {

extractors.put(ResourceFileType.PDF, new PdfExtractor());

extractors.put(ResourceFileType.PPT, new PPTExtractor());

extractors.put(ResourceFileType.WORD, new WordExtractor());

}

public static Extractor getExtractor(ResourceFileType type) {

return extractors.get(type);

}

}

public class ToolApplication {

public static void main(String[] args) {

List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);

for (ResourceFile resourceFile : resourceFiles) {

Extractor extractor = ExtractorFactory.getExtractor(resourceFile.getType());

extractor.extract2txt(resourceFile);

}

}

private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {

List<ResourceFile> resourceFiles = new ArrayList<>();

//…根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)

resourceFiles.add(new PdfFile(“a.pdf”));

resourceFiles.add(new WordFile(“b.word”));

resourceFiles.add(new PPTFile(“c.ppt”));

return resourceFiles;

}

}

当需要添加新的功能的时候,比如一个压缩功能,只需要添加一个Compressor的接口,然后再创建一个对应的工厂类,并且在入口类ToolApplication中添加即可

对于,只有几种对应的处理方式的话,使用工厂类的确是一种好选择,对于有几十种的方式,其实使用访问者模式会更好,因为其将分类交给了类本身来判断

本章回顾

总体来说,访问者模式,难以理解,应用场景有限,不是特别需要的话,就没有没必要在项目中使用,对于一些处理模式不多的例子,推荐使用工厂模式来设计实现

除此外,我们还说了Double Dispatch,就是类和参数的分派特性,

在Double Dispatch面向对象编程语言中,方法调用可以理解为一种消息传递,一个对象调用另一个对象的方法,给其发送一条消息

在Single Dispatch中,执行哪个对象的方法,根据对象的运行时的类型决定的,执行对象的哪个方法,根据方法参数的编译时的类型决定的,

Double Dispatch,指的是执行哪个对象的方法,根据运行时的类型决定的,执行对象的哪个方法,也是运行时的类型

课后思考

1.访问者模式将操作和对象分离,是否违背面向对象原则

2,解释Single Dispatch代码中,如果把SingleDispatchClass代码改为如下的,DemoMain的输出如何?

1.其实没有什么违背,我们这样做,其实操作和对象本身还是挂着勾的,只是将两者操作简单的剥离了出去,就好比Service层的service类和Domain类一样,而且提高了代码的可维护,可扩展性

2.皆为C,而非P,改为上面的方法后,虽然找到了overloadFunction(P p),但是又变为了执行哪个对象的方法的问题了,所以还是C

发表评论

邮箱地址不会被公开。 必填项已用*标注