在没有特别必要的情况下,建议你不要使用访问者模式。

带你“发明”访问者模式

  1. public abstract class ResourceFile {
  2. protected String filePath;
  3. public ResourceFile(String filePath) {
  4. this.filePath = filePath;
  5. }
  6. abstract public void accept(Visitor vistor);
  7. }
  8. public class PdfFile extends ResourceFile {
  9. public PdfFile(String filePath) {
  10. super(filePath);
  11. }
  12. @Override
  13. public void accept(Visitor visitor) {
  14. visitor.visit(this);
  15. }
  16. //...
  17. }
  18. //...PPTFile、WordFile跟PdfFile类似,这里就省略了...
  19. public interface Visitor {
  20. void visit(PdfFile pdfFile);
  21. void visit(PPTFile pdfFile);
  22. void visit(WordFile pdfFile);
  23. }
  24. public class Extractor implements Visitor {
  25. @Override
  26. public void visit(PPTFile pptFile) {
  27. //...
  28. System.out.println("Extract PPT.");
  29. }
  30. @Override
  31. public void visit(PdfFile pdfFile) {
  32. //...
  33. System.out.println("Extract PDF.");
  34. }
  35. @Override
  36. public void visit(WordFile wordFile) {
  37. //...
  38. System.out.println("Extract WORD.");
  39. }
  40. }
  41. public class Compressor implements Visitor {
  42. @Override
  43. public void visit(PPTFile pptFile) {
  44. //...
  45. System.out.println("Compress PPT.");
  46. }
  47. @Override
  48. public void visit(PdfFile pdfFile) {
  49. //...
  50. System.out.println("Compress PDF.");
  51. }
  52. @Override
  53. public void visit(WordFile wordFile) {
  54. //...
  55. System.out.println("Compress WORD.");
  56. }
  57. }
  58. public class ToolApplication {
  59. public static void main(String[] args) {
  60. Extractor extractor = new Extractor();
  61. List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
  62. for (ResourceFile resourceFile : resourceFiles) {
  63. resourceFile.accept(extractor);
  64. }
  65. Compressor compressor = new Compressor();
  66. for(ResourceFile resourceFile : resourceFiles) {
  67. resourceFile.accept(compressor);
  68. }
  69. }
  70. private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {
  71. List<ResourceFile> resourceFiles = new ArrayList<>();
  72. //...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)
  73. resourceFiles.add(new PdfFile("a.pdf"));
  74. resourceFiles.add(new WordFile("b.word"));
  75. resourceFiles.add(new PPTFile("c.ppt"));
  76. return resourceFiles;
  77. }
  78. }

重新来看访问者模式

允许一个或者多个操作应用到一组对象上,解耦操作和对象本身.

操作指:

  1. public interface Visitor {
  2. void visit(PdfFile pdfFile);
  3. void visit(PPTFile pdfFile);
  4. void visit(WordFile pdfFile);
  5. }

对象指:

  • PdfFile 等

image.png

总结

一般来说,访问者模式针对的是一组类型不同的对象(PdfFile、PPTFile、WordFile)。不过,尽管这组对象的类型是不同的,但是,它们继承相同的父类(ResourceFile)或者实现相同的接口。在不同的应用场景下,我们需要对这组对象进行一系列不相关的业务操作(抽取文本、压缩等),但为了避免不断添加功能导致类(PdfFile、PPTFile、WordFile)不断膨胀,职责越来越不单一,以及避免频繁地添加功能导致的频繁代码修改,我们使用访问者模式,将对象与操作解耦,将这些业务操作抽离出来,定义在独立细分的访问者类(Extractor、Compressor)中。