zoukankan      html  css  js  c++  java
  • 设计模式之美学习-行为型-访问者模式(三十二)

    什么是访问者模式

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

    因为它难理解、难实现,应用它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很少被用到,在没有特别必要的情况下,不建议使用

    应用场景

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

    需求

    假设我们从网站上爬取了很多资源文件,它们的格式有三种:PDF、PPT、Word。我们现在要开发一个工具来处理这批资源文件。这个工具的其中一个功能是,把这些资源文件中的文本内容抽取出来放到 txt 文件中

    实现方式一

    /**
     * 抽象的处理方式
     */
    public abstract class ResourceFile {
        protected String filePath;
    
        public ResourceFile(String filePath) {
            this.filePath = filePath;
        }
    
        public abstract void extract2txt();
    }
    
    /**
     * 处理ppt文件
     */
    public class PPTFile extends ResourceFile {
        public PPTFile(String filePath) {
            super(filePath);
        }
    
        /**
         * 读取并放入text
         */
        @Override
        public void extract2txt() {
            //...省略一大坨从PPT中抽取文本的代码...
            //...将抽取出来的文本保存在跟filePath同名的.txt文件中...
            System.out.println("Extract PPT.");
        }
    }
    
    /**
     * 处理pdf文件
     */
    public class PdfFile extends ResourceFile {
        public PdfFile(String filePath) {
            super(filePath);
        }
    
        @Override
        public void extract2txt() {
            //...
            System.out.println("Extract PDF.");
        }
    }
    
    /**
     * 处理word
     */
    public class WordFile extends ResourceFile {
        public WordFile(String filePath) {
            super(filePath);
        }
    
        @Override
        public void extract2txt() {
            //...
            System.out.println("Extract WORD.");
        }
    }
    
    // 运行结果是:
    // Extract PDF.
    // Extract WORD.
    // Extract PPT.
    public class ToolApplication {
        public static void main(String[] args) {
            /**
             * 获得所有文件读取并写入text
             */
            List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
            for (ResourceFile resourceFile : resourceFiles) {
                resourceFile.extract2txt();
            }
        }
    
    
        /**
         * 获得所有文件
         * @param resourceDirectory
         * @return
         */
        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;
        }
    }

    缺点:当扩展功能增加压缩、提取文件元信息(文件名、大小、更新时间等等)构建索引等一系列的功能,那如果我们继续按照上面的实现思路,就会存在这样几个问题:

    1.违背开闭原则,添加一个新的功能,所有类的代码都要修改;

    2.虽然功能增多,每个类的代码都不断膨胀,可读性和可维护性都变差了;

    3.把所有比较上层的业务逻辑都耦合到 PdfFile、PPTFile、WordFile 类中,导致这些类的职责不够单一,变成了大杂烩。

    重构方案一

    /**
     * 抽象的父类处理
     */
    public abstract class ResourceFile {
        protected String filePath;
        public ResourceFile(String filePath) {
            this.filePath = filePath;
        }
    }
    
    public class PdfFile extends ResourceFile {
        public PdfFile(String filePath) {
            super(filePath);
        }
        //...
    }
    
    /**
     * 将所有文件的处理抽到extrcaor
     */
    //...PPTFile、WordFile代码省略...
    public class Extractor {
        /**
         * 提取ppt写入txt
         * @param pptFile
         */
        public void extract2txt(PPTFile pptFile) {
            //...
            System.out.println("Extract PPT.");
        }
    
        /**
         * 提取pdf写入txt
         * @param pdfFile
         */
        public void extract2txt(PdfFile pdfFile) {
            //...
            System.out.println("Extract PDF.");
        }
    
        /**
         * 提取word写入txt
         * @param wordFile
         */
        public void extract2txt(WordFile wordFile) {
            //...
            System.out.println("Extract WORD.");
        }
    }
    
    public class ToolApplication {
        public static void main(String[] args) {
            //创建extrator
            
            Extractor extractor = new Extractor();
            //获取所有处理文件
            List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
            //调用extractor方法
            for (ResourceFile resourceFile : resourceFiles) {
                //这里会编译不过 因为根据重载父类类型不能替代子类类型
                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;
        }
    }

    当我们需要增加功能只需要增加对应的Extractor就行了,但是红色部分呢会编译不过

    重构方案二

    public abstract class ResourceFile {
        protected String filePath;
        public ResourceFile(String filePath) {
            this.filePath = filePath;
        }
        //传入对应的extrator
        abstract public void accept(Extractor extractor);
    }
    
    public class PdfFile extends ResourceFile {
        public PdfFile(String filePath) {
            super(filePath);
        }
    
        @Override
        public void accept(Extractor extractor) {
            //根据重载 路由到对应的处理方法
            extractor.extract2txt(this);
        }
    
        //...
    }
    
    //...PPTFile、WordFile跟PdfFile类似,这里就省略了...
    //...Extractor代码不变...
    
    public class ToolApplication {
        public static void main(String[] args) {
            //创建Extractor
            Extractor extractor = new Extractor();
            List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
            for (ResourceFile resourceFile : resourceFiles) {
                //调用accept传入extrator
                resourceFile.accept(extractor);
            }
        }
    
        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;
        }
    }

    缺点:这样面临的问题 当扩展压缩等功能还是需要在对应的file处理里面增加accept 还是会修改代码.

    优化方案四

    //抽象的文件处理类
    public abstract class ResourceFile {
        protected String filePath;
        public ResourceFile(String filePath) {
            this.filePath = filePath;
        }
        abstract public void accept(Visitor vistor);
    }
    
    public class PdfFile extends ResourceFile {
        public PdfFile(String filePath) {
            super(filePath);
        }
    
        //文件处理
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    
        //...
    }
    //...PPTFile、WordFile跟PdfFile类似,这里就省略了...
    
    
    /**
     * 抽象的文件处理访问类
     */
    public interface Visitor {
        void visit(PdfFile pdfFile);
        void visit(PPTFile pdfFile);
        void visit(WordFile pdfFile);
    }
    
    /**
     * 写入到ext 的访问者
     */
    public class Extractor implements Visitor {
        @Override
        public void visit(PPTFile pptFile) {
            //...
            System.out.println("Extract PPT.");
        }
    
        @Override
        public void visit(PdfFile pdfFile) {
            //...
            System.out.println("Extract PDF.");
        }
    
        @Override
        public void visit(WordFile wordFile) {
            //...
            System.out.println("Extract WORD.");
        }
    }
    
    /**
     * 压缩访问则
     */
    public class Compressor implements Visitor {
        @Override
        public void visit(PPTFile pptFile) {
            //...
            System.out.println("Compress PPT.");
        }
    
        @Override
        public void visit(PdfFile pdfFile) {
            //...
            System.out.println("Compress PDF.");
        }
    
        @Override
        public void visit(WordFile wordFile) {
            //...
            System.out.println("Compress WORD.");
        }
    
    }
    
    public class ToolApplication {
        public static void main(String[] args) {
            //写入
            Extractor extractor = new Extractor();
            List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
            for (ResourceFile resourceFile : resourceFiles) {
                resourceFile.accept(extractor);
            }
           //压缩
            Compressor compressor = new Compressor();
            for(ResourceFile resourceFile : resourceFiles) {
                resourceFile.accept(compressor);
            }
        }
    
        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;
        }
    }
  • 相关阅读:
    微软外服 AlI In One
    js 循环多次和循环一次的时间的性能对比 All In One
    vue inject All In One
    Excel 表格数据倒置 All In One
    SVG tickets All In One
    OH MY ZSH All In One
    js array for loop performance compare All In One
    mac terminal show You have new mail All In one
    新闻视频 26 制作母版页
    转自牛腩 母版页和相对路径
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12738792.html
Copyright © 2011-2022 走看看