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;
        }
    }
  • 相关阅读:
    关于数据库中浮点运算、保留小数和时间计算
    几个常用的操作
    数字转换为字符串
    Dll控件出错的处理办法
    小巧的服务程序源码(转)
    DELPHI中MDI子窗口的关闭和打开
    用Delphi创建服务程序
    Delphi如何获取QQ2010聊天窗口句柄?
    拖动Form上的图片,Form一起动
    仿药易通输入单位信息后如果没有则自动加入功能
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12738792.html
Copyright © 2011-2022 走看看