求推荐专业的网站建设开发,全网营销推广平台,做网站应该注意什么,深圳建网站兴田德润很好概述
上篇文章#xff0c;我们学习了访问者模式的原理和实现#xff0c;并还原了访问者模式诞生的过程。总体来说#xff0c;这个模式的代码实现比较难#xff0c;所以应用场景不多。从应用开发的角度来说#xff0c;它的确不是我们学习的重点。
本章#xff0c;我们把…概述
上篇文章我们学习了访问者模式的原理和实现并还原了访问者模式诞生的过程。总体来说这个模式的代码实现比较难所以应用场景不多。从应用开发的角度来说它的确不是我们学习的重点。
本章我们把访问者模式作为引子一块讨论下这样两个问题
为什么支持双分派的语言不需要访问者模式除了访问者模式上篇文章的例子还有其他的实现方式吗 为什么支持双分派的语言不需要访问者模式
实际上讲到访问者模式大部分书籍或资料都会讲到 Double Dispatch中文翻译为双分派。虽然学习访问者模式并不用非得理解这个概念但是为了让你在查看其他书籍或资料时不会卡在这个概念上本章在这里讲一下。
此外个人觉得学习 Double DIspatch 可以加深你对访问者模式的理解。
既然有 Double Dispatch对应的就有 Signle Dispatch。
所谓 Signle Dispatch指的是执行哪个对象的方法根据对象的运行时类型来决定执行对象的哪个方法根据方法参数的编译时类型来决定。所谓 Double Dispatch指的是执行哪个对象的方法根据对象的运行时类型来决定执行对象的哪个方法根据方法参数的运行时类型来决定。
如何理解 “Dispatch” 这个单词呢 在面向对象编程语言中可以把方法调用理解为一种消息传递也就是 “Dispatch”。一个对象调用另一个对象的方法就相当于给它发送一条消息。这条消息要包含对象名、方法名、方法参数。
如何理解 “Single” “Double” 这两个单词呢 “Single” “Double” 指的是执行那个对象的哪个方法跟几个因素的运行时类型有关。
Signle Dispatch 之所以称为 “Single”是因为执行哪个对象的哪个方法只跟 “对象” 的运行时类型有关。Double Dispatch 之所以称为 “Double”是因为执行哪个对象的哪个方法跟 “对象” 和 “方法参数” 两者的运行时类型有关。
具体到编程语言的语法机制Signle Dispatch 和 Double Dispatch 跟多态和函数重载直接相关。当前主流的面向对象编程语言比如Java、C、C#都只支持 Signle Dispatch 不支持 Double Dispatch。
接下来拿 Java 语言来举例说明下。
Java 支持多态代码可以在运行时获得对象的实际类型也就是前面提到的运行时类型然后根据实际类型决定调用哪个方法。尽管 Java 支持函数重载但 Java 设计的函数重载并不是在运行时根据传递进函数的参数的实际类型来决定调用重载函数。而是在编译时根据传递进函数的参数的声明类型也就是前面提到的编译时类型来决定调用哪个重载函数。也就是说具体执行哪个对象的哪个方法只跟对象的运行时类型有关跟函数参数的运行时类型无关。所以Java 语言只支持 Signle Dispatch 。
这么说可能比较抽象下面举个例子来说明下。
public class ParentClass {public void f() {System.out.println(I am ParentClasss f().);}
}public class ChildClass extends ParentClass {Overridepublic void f() {System.out.println(I am ChildClasss f().);}
}public class SingleDispatchCLass {public void polymorphismFunction(ParentClass p) {p.f();}public void overloadsFunction(ParentClass p) {System.out.println(I am overloadFunction(ParentClass p));}public void overloadsFunction(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.overloadsFunction(p); // 执行对象的哪个方法由参数对象的声明类型决定}
}// 代码执行结果
I am ChildClasss f().
I am overloadFunction(ParentClass p)上面的代码中demo.polymorphismFunction(p) 执行 p 的实际类型的 f() 函数也就是 ChildClass 的 f() 函数。 demo.overloadsFunction(p) 匹配的是重载函数中的 overloadsFunction(ParentClass p) 也就是根据 p 的声明类型来决定匹配哪个重载函数。
假设 Java 语言支持 Double Dispatch那下面的代码摘抄至上篇文章中 extractor.extract2txt(resourceFile) 的就不会报错。代码运行时根据参数resourceFile的实际类型PdfFile、PptFile、WordFile来决定使用 extract2txt 的三个重载函数中的哪一个。下面的代码就能正常运行了也就不需要访问者模式了。这也回达了为什么支持 Double Dispatch 的语言不需要访问者模式。
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath filePath;}
}public class PptFile extends ResourceFile {public PptFile(String filePath) {super(filePath);}// ...
}public class PdfFile extends ResourceFile {public PptFile(String filePath) {super(filePath);}// ...
}public class WordFile extends ResourceFile {public PptFile(String filePath) {super(filePath);}// ...
}public class Extractor {public void extract2txt(PptFile pptFile) {// ...System.out.println(Extract PPT.);}public void extract2txt(PdfFile pdfFile) {// ...System.out.println(Extract PDF.);}public void extract2txt(WordFile wordFile) {// ...System.out.println(Extract Word.);}
}public class ToolApplication {public static void main(String[] args) {Extractor extractor new Extractor();ListResourceFile resourceFiles listAllResourceFiles(args[0]);for (ResourceFile resourceFile : resourceFiles) {extractor.extract2txt(resourceFile);}}private static ListResourceFile listAllResourceFiles(String resourceDirectory) {ListResourceFile resourceFiles new ArrayList();// ... 根据后缀ppt/pdf/word)由工厂方法创建不同类型的类对象PptFile/PdfFile/WordFileresourceFiles.add(new PdfFile(a.pdf));resourceFiles.add(new PptFile(b.ppt));resourceFiles.add(new WordFile(c.word));return resourceFiles;}
}
除了访问者模式上一节的例子还有其他的实现方案吗
上篇文章通过一个例子给你展示了访问者模式是如何一步一步设计出来的。我们在回顾下那个例子。我们从网址站上爬取了很多资源文件它们的格式有PDF、PPT、Word。我们要开发一个工具来处理这批资源文件这其实就包含抽取文本内容、压缩资源文件、提取文件信息等。
实际上开发这个工具有很多种代码设计和实现思路。为了讲解访问者模式上篇文章我们使用了访问者模式来实现。实际上还有其他的实现方法比如可以利用工程模式来实现定义一个包含 extract2txt() 函数的 Extractor 接口。PdfExtractor、PptExtractor、WordExtractor 实现 Extractor 接口并且在各自的 extract2txt() 函数中分别实现 pdf、ppt、word 格式文件的文本内容抽取 。ExtractorFactory 工厂类根据不同的文件类型返回不同的 Extractor。
这个实现思路其实更加简单代码如下所示。
public enum ResourceFileType {PDF,PPT,WORD;
}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);}Overridepublic ResourceFileType getType() {return ResourceFileType.PDF;}// ...
}public class PptFile extends ResourceFile {public PptFile(String filePath) {super(filePath);}Overridepublic ResourceFileType getType() {return ResourceFileType.PPT;}// ...
}public class WordFile extends ResourceFile {public WordFile(String filePath) {super(filePath);}Overridepublic ResourceFileType getType() {return ResourceFileType.WORD;}// ...
}public interface Extractor {void extract2txt(ResourceFile resourceFile);
}public class PdfExtractor implements Extractor {Overridepublic void extract2txt(ResourceFile resourceFile) {// ...}
}public class PptExtractor implements Extractor {Overridepublic void extract2txt(ResourceFile resourceFile) {// ...}
}public class WordExtractor implements Extractor {Overridepublic void extract2txt(ResourceFile resourceFile) {// ...}
}public class ExtractorFactory {private static final MapResourceFileType, 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) {ListResourceFile resourceFiles listAllResourceFiles(args[0]);for (ResourceFile resourceFile : resourceFiles) {Extractor extractor ExtractorFactory.getExtractor(resourceFile.getType());extractor.extract2txt(resourceFile);}}private static ListResourceFile listAllResourceFiles(String resourceDirectory) {ListResourceFile resourceFiles new ArrayList();// ... 根据后缀ppt/pdf/word)由工厂方法创建不同类型的类对象PptFile/PdfFile/WordFileresourceFiles.add(new PdfFile(a.pdf));resourceFiles.add(new PptFile(b.ppt));resourceFiles.add(new WordFile(c.word));return resourceFiles;}
}当需要添加新功能时比如压缩文件类似抽取文本内容功能的实现代码只需要添加一个 Compressor 接口PdfCompressor、PptCompressor、WordCompressor 三个实现类以及创建它们的 CompressorFactory 工厂类即可。唯一需要修改的只有最上层的 ToolApplication。基本上符合 “对扩展开放、对修改关闭” 的设计原则。
对于资源文件处理工具的例子如果工具提供的功能并不是很多只有几个而已那更推荐使用工程模式的实现方式比较代码清晰、易懂。相反如果工具提供非常多的功能比如有十几个那更推荐使用访问者模式因为访问者模式需要定义的类要比工程模式的少很多类太多也会影响代码的可维护性。
总结
总体来说访问者模式难以理解应用场景有限不是特别必需不建议在项目中使用它。所以对于上篇文章的处理资源文件的例子更推荐使用工厂设计模式来设计和实现。
本章重点讲解了 Double Dispatch。在面向对象编程语言中方法调用可以理解为一种消息传递Dispatch。一个对象调用另一个对象的方法就相当于给它发送一条消息这条消息起码要包含对象名、方法名和方法参数。
所谓 Signle Dispatch指的是执行哪个对象的方法根据对象的运行时类型来决定执行对象的哪个方法根据方法参数的编译时类型来决定。所谓 Double Dispatch指的是执行哪个对象的方法根据对象的运行时类型来决定执行对象的哪个方法根据方法参数的运行时类型来决定。
具体到编程语言的语法机制Signle Dispatch 和 Double Dispatch 跟多态和函数重载直接相关。当前主流的面向对象编程语言如Java、C都只支持 Signle Dispatch不支持 Double Dispatch。