设计模式之访问者模式

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

访问者模式

由于它难理解、难实现。应用它会导致代码的可读性、可维护性变差,所以在开发中很少被用到。

定义

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

类图

设计模式之访问者模式_第1张图片

实现

需求: 对PDF、Word格式进行处理,把里面的数据抽取到Txt文本文件中。

1. 定义访问者接口

    /**
     * 访问者接口
     * 包含两个重载函数,分别处理两种不同类型的资源文件。具体选择哪个文件处理,需要在编译时进行绑定,
     * 在各个Resource实现内部,会把this传递给当前方法,这样就可以知道绑定的对象是哪个,选用合适的重载方法进行处理。
     * 具体做什么业务处理,由实现这个visitor接口的具体实现类来决定。
     *
     */
    public interface Visitor {
        /**
         * 定义PDF类型
         * @param pdfResourceFile
         */
        void visit(PdfResourceFile pdfResourceFile);
    
        /**
         * 定义Word类型
         * @param wordResourceFile
         */
        void visit(WordResourceFile wordResourceFile);
    }

2. 定义Visitor实现类

    /**
     * 定义压缩功能访问者实现类
     *
     */
    public class CompressorVisitor implements Visitor{
        /**
         * PDF类型压缩
         * @param pdfResourceFile
         */
        @Override
        public void visit(PdfResourceFile pdfResourceFile) {
            System.out.println("compress pdf");
        }
    
        /**
         * Word类型压缩
         * @param wordResourceFile
         */
        @Override
        public void visit(WordResourceFile wordResourceFile) {
            System.out.println("compress word");
        }
    }
    /**
     * 文件抽取实现类
     *
     */
    public class ExtractVisitor implements Visitor {
        /**
         * PDF文件抽取
         * @param pdfResourceFile
         */
        @Override
        public void visit(PdfResourceFile pdfResourceFile) {
            System.out.println("PDF EXTRACT.");
        }
    
        /**
         * word文件抽取
         * @param wordResourceFile
         */
        @Override
        public void visit(WordResourceFile wordResourceFile) {
            System.out.println("WORD EXTRACT.");
        }
    }

3. 定义ResourceFile抽象类

    /**
     * 资源抽象类
     */
    public abstract class ResourceFile {
        protected String filePath;
        public ResourceFile(String filePath) {
            this.filePath = filePath;
        }
    
        /**
         * 这里可以接收visitor的标准实现,具体功能由Visitor具体实现类决定。
         * 可以接收CompressorVisitor用来压缩文件、ExtractVisitor用来抽取文件内容,将来还会有索引文件建立等功能需求,只需要实现visitor接口即可。
         * 功能的实现
         * @param visitor
         */
        public abstract void accept(Visitor visitor);
    }

4. 定义ResourceFile实现类

    /**
     * PDF资源实现类
     */
    public class PdfResourceFile extends ResourceFile {
        public PdfResourceFile(String filePath) {
            super(filePath);
        }
    
        /**
         * 这里可以接收visitor的标准实现,具体功能由Visitor具体实现类决定。
         * @param visitor
         */
        @Override
        public void accept(Visitor visitor) {
            // 重点。需要传入此时的对象,否则,visitor不晓得调用哪个重载方法
            visitor.visit(this);
        }
    }
    /**
     * Word资源实现类
     */
    public class WordResourceFile extends ResourceFile {
        public WordResourceFile(String filePath) {
            super(filePath);
        }
    
        /**
         *
         * @param visitor
         */
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

5. Main

    /**
     * 访问者模式(GoF):允许一个或者多个操作应用到一组对象上,解耦操作和对象本身
     * 应用场景:一般来说,访问者模式针对的是一组类型不同的对象(PDF、Word...) ,虽然它们类型不同,但是拥有相同父类或实现相同接口。
     * 在不同的场景下,我们需要对这组对象进行一系列不相关的业务操作,但为了避免不断添加功能导致类(PdfResourceFile、WordResourceFile)不断膨胀,职责越来越不单一
     * 以及频繁地添加功能导致的频繁代码修改,我们使用访问者模式,将对象与操作解耦,将这些业务操作抽离出来,定义在独立的细分的访问者类(CompressorVisitor、ExtractVisitor)中
     */
    public class M {
        public static void main(String[] args) {
            Visitor compress = new CompressorVisitor();
            Visitor extractor = new ExtractVisitor();
            List sourceFiles = listAllResourceFiles("h");
            for (ResourceFile sourceFile : sourceFiles) {
                sourceFile.accept(compress);
            }
    
            for (ResourceFile sourceFile : sourceFiles) {
                sourceFile.accept(extractor);
            }
    
        }
    
        private static List listAllResourceFiles(String resourcePath) {
            ArrayList resourceFiles = new ArrayList<>();
            resourceFiles.add(new PdfResourceFile("a.pdf"));
            resourceFiles.add(new WordResourceFile("b.word"));
            return resourceFiles;
        }
    }

扩展

    /**
     * 功能扩展一:建立索引文件
     */
    public class IndexVisitor implements Visitor{
        @Override
        public void visit(PdfResourceFile pdfResourceFile) {
            System.out.println("对PDF建立索引");
        }
    
        @Override
        public void visit(WordResourceFile wordResourceFile) {
            System.out.println("对Word建立索引");
        }
    }
    
    
    
    
    // 索引文件
    IndexVisitor indexVisitor = new IndexVisitor();
    for (ResourceFile sourceFile : sourceFiles) {
        sourceFile.accept(indexVisitor);
    }

总结

  1. 一般来说,访问者模式 针对的是一组类型不同的对象。不过,尽管这组对象的类型是不同的,但是,它们继承相同的父类或者实现相同的接口。
  2. 访问者模式允许一个或者多个操作应用到一组对象上,设计意图是解耦操作对象本身,保持类职责单一、满足开闭原则以及降低代码的复杂性。
  3. 对于访问者模式,学习的主要难点在代码实现。而代码实现比较复杂的主要原因是,函数重载在大部分面向对象编程语言中是静态绑定的。也就是说,调用类的哪个重载函数,是在编译期间,由参数的声明类型决定的,而非运行时,根据参数的实际类型决定的。
  4. 正是因为代码实现难理解,所以,在项目中应用这种模式,会导致代码的可读性比较差。

你可能感兴趣的:(设计模式,设计模式,访问者模式)