如果像是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