设计模式-Visitor模式(访问者模式)

2023-11-08

什么是访问者模式

将数据结构与处理分离开来。

比如一个人,他有自己的名字,他可以吃东西。这里的“人”就是一个数据结构,名字是属性,吃东西是行为方法(处理)。

public class Person {
    private String name;
    
    public void eat() {
        System.out.println("自己吃东西");
    }
}

将数据结构中的处理分离出去,就像这样换成一个accept方法,接收一个访问者,然后将自己作为参数,调用访问者的visit方法

public class Person {
    private String name;

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

在访问者中,调用相应的visit方法,让访问者“帮我吃东西”

public class Visitor {
    public void visit(Person person) {
        System.out.println("观察者帮person吃");
    }
}

另一个例子

在下面这个例子中,使用Composite模式中用到的那个文件和文件夹的例子作为访问者要访问的数据结构。

Visitor类,表示访问者的抽象类。访问者依赖于它所访问的数据结构(即File类和Directory类)

public abstract class Visitor {
    public abstract void visit(File file);
    public abstract void visit(Directory directory);
}

Element接口,只有一个accept方法,表示实现了这个接口的类,都接受访问者来访问自己(把本类的处理分离出去)。

public abstract class Element {
    public abstract void accept(Visitor visitor);
}

Entry类,与Composite模式中的Entry类基本一致,表示抽象容器,用来实现File和Directory的一致性。

public abstract class Entry extends Element{
    public abstract String getName();
    public abstract int getSize();
    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException();
    }

    public Iterator iterator() throws FileTreatmentException {
        throw new FileTreatmentException();
    }

    public String toString() {
        return getName() + " (" + getSize() + ")";
    }
}

File类,实现了accept方法,传入一个访问者,然后通过多态调用相应的visit方法,将this作为参数传给访问者。

public class File extends Entry {

    private String name;
    private int size;

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

Directory类,也实现了accept方法,与File的做法一样。

public class Directory extends Entry {
    private String name;
    private ArrayList directory = new ArrayList();

    public Directory(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        int size = 0;
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry) it.next();
            size += entry.getSize();
        }
        return size;
    }

    @Override
    public Entry add(Entry entry) throws FileTreatmentException {
        directory.add(entry);
        return this;
    }

    @Override
    public Iterator iterator() throws FileTreatmentException {
        return directory.iterator();
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

ListVisitor类,是Visitor的子类,表示具体的访问者。

  1. visit(File file)方法是用来实现“对File类的实例要进行的处理”,这里只是简单的输出文件夹名。
  2. visit(Directory directory)方法实现了“对Directory类的实例要进行的处理”,这里会递归调用directory中的file和其中的子文件夹,即accept方法与visit方法之间的相互递归调用
public class ListVisitor extends Visitor {

    private String currentdir = "";

    @Override
    public void visit(File file) {
        System.out.println(currentdir + "/" + file);
    }

    @Override
    public void visit(Directory directory) {
        System.out.println(currentdir + "/" + directory);
        String saveDir = currentdir;
        currentdir = currentdir + "/" + directory.getName();
        Iterator iterator = directory.iterator();
        while (iterator.hasNext()) {
            Entry entry = (Entry) iterator.next();
            entry.accept(this);
        }
        currentdir = saveDir;
    }
}

异常类,这个不是重点

public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException() {
    }

    public FileTreatmentException(String message) {
        super(message);
    }
}

测试类

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Making root entries...");
            Directory rootDir = new Directory("root");
            Directory bindir = new Directory("bin");
            Directory tmpdir = new Directory("tmp");
            Directory usrdir = new Directory("usr");
            rootDir.add(bindir);
            rootDir.add(tmpdir);
            rootDir.add(usrdir);

            bindir.add(new File("vi", 10000));
            bindir.add(new File("latex", 20000));
            rootDir.accept(new ListVisitor());

        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }
}

双重分发

在Visitor模式中,ConcreteElement和ConcreteVisitor这两个角色共同决定了实际进行的处理。这种消息分发的方式一般被称为双重分发

开闭原则——对扩展开放,对修改关闭

开闭原则(The Open-Closed Principle,OCP),就是在不修改现有代码的前提下进行扩展。比如这里的File和Directory就具有高可复用性,可作为组件复用,如果需要改变对其的处理方式,只需要增加ConcreteVisitor类,而不用修改File和Directory(File的accpet方法不用改变)

优缺点

优点:易于增加ConcreteVisitor角色
缺点:难以增加ConcreteElement角色,如果要增加一个Device类继承了Entry类,那么它肯定也要实现accept方法,那抽象访问者肯定要加一个专门给Device类的访问方法visit(Device device),这样全部的ConcreteVisitor都要实现这个新增方法,非常麻烦。

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式-Visitor模式(访问者模式) 的相关文章

  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • 如何找到给定字符串的最长重复子串

    我是java新手 我被分配寻找字符串的最长子字符串 我在网上研究 似乎解决这个问题的好方法是实现后缀树 请告诉我如何做到这一点或者您是否有任何其他解决方案 请记住 这应该是在 Java 知识水平较低的情况下完成的 提前致谢 附 测试仪字符串
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • 禁止的软件包名称:java

    我尝试从数据库名称为 jaane 用户名 Hello 和密码 hello 获取数据 错误 java lang SecurityException Prohibited package name java at java lang Class
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • Java执行器服务线程池[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如果我使用 Executor 框架在
  • Google App Engine 如何预编译 Java?

    App Engine 对应用程序的 Java 字节码使用 预编译 过程 以增强应用程序在 Java 运行时环境中的性能 预编译代码的功能与原始字节码相同 有没有详细的信息这是做什么的 我在一个中找到了这个谷歌群组消息 http groups
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r
  • 编译器抱怨“缺少返回语句”,即使不可能达到缺少返回语句的条件

    在下面的方法中 编译器抱怨缺少退货声明即使该方法只有一条路径 并且它包含一个return陈述 抑制错误需要另一个return陈述 public int foo if true return 5 鉴于Java编译器可以识别无限循环 https
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s

随机推荐

  • goland os.Open 路径错误

    os Open 报错原因 Open不是当前目录下查找 项目取的是项目根目录 不是执行目录 证明如下 exPath os Getwd fmt Println expath exPath file err os Open exPath file
  • 504 错误码排查

    当出现 504 错误码时 表示请求超时 服务器无法及时响应请求 需要检查下应用是否有什么耗时的操作 比如是否出现了 SQL 慢查询 是否接口发生死循环 是否出现死锁等 同时需要关注服务器系统负载高不高 网络异常 接口原本好好的 突然出现超时
  • JAVA基础算法——将字符串按照字典倒序排序并输出

    JAVA基础算法 将字符串按照字典倒序输出 作者 飞不高的鱼 转载请注明出处 import java util Arrays import java util Comparator 将已知的乱序字符串按照字典倒序排序输出 author xi
  • 矩阵的投影、线性拟合与最小二乘法

    概要 投影矩阵 如果一个b向量进行矩阵运算 Pb 那么向量b就会投影要A的列空间的最近点 目录 一 矩阵的四大基础子空间 二 投影矩阵 三 最小二乘法 一 矩阵的四大基础子空间 一个矩阵有4个子空间 分别是行空间 零空间 列空间和左零空间
  • C#中的委托、事件与接口

    C 中的委托 事件与接口 一 委托 一 委托链 二 匿名函数 三 Lamda语句 二 事件 三 接口 一 委托 委托 将方法以变量的形式传递 以方法的形式执行 注意 赋值方法的返回类型 参数要与委托的一致 如委托定义的参数类型是string
  • 微信小程序上传文件组件

    微信小程序上传文件 一 说明 该拍照组件带有微信授权相机功能 会结合后端接口 将上传的图片以数组集合的形式传值给父级页面 注意 组件适用于 单独上传图片 不携带参数 结合后端接口返回路径之后 再调用另外的保存接口 携带参数与图片提交的场景
  • 抓取第三方网站数据

    最近需要把某网站的统计数据聚合到我们自己的系统里 但是该网站没有提供标准API 所以就尝试自己抓取了一下 本文总结一下一般的方法 分析服务地址 通常网站有2种做法 一种是后端渲染 直接把渲染后的完整界面呈现在浏览器 另一种前端是静态页面 通
  • mybatis plus中的${ew.sqlSegment},${ew.sqlSelect},${ew.customSqlSegment},${ew.sqlSet}使用

    ew是mapper方法里的 Param Constants WRAPPER Wrapper queryWrapper对象 首先判断ew emptyOfWhere是否存在where条件 有的话再拼接上去 ew customSqlSegment
  • java毕业设计——基于JSP+JavaBean+sqlserver的在线购物系统设计与实现(毕业论文+程序源码)——在线购物系统

    基于JSP JavaBean sqlserver的在线购物系统设计与实现 毕业论文 程序源码 大家好 今天给大家介绍基于JSP JavaBean sqlserver的在线购物系统设计与实现 文章末尾附有本毕业设计的论文和源码下载地址哦 需要
  • 品味树莓派:GPIO Zero库使用入门

    文章目录 目的 基础说明 入门使用 LED PWMLED Button 更多入门例程 类基础说明 注意事项 总结 目的 树莓派有很多GPIO口可供用户使用 官方同时也提供了一些方式来操作这些IO口 其中目前主要推荐的是基于Python的GP
  • 1、什么是Shader

    什么是Shader Shader 中文名为着色器 Shader其实就是专门用来渲染图形的一种技术 通过shader 我们可以自定义显卡渲染画面的算法 使画面达到我们想要的效果 小到每一个像素点 大到整个屏幕 Shader分为两类 顶点Sha
  • 使用Gradle构建SpringBoot工程系列:第八篇:使用spring-data-jpa 实现数据持久化

    本篇文章是SpringBoot 系列文章的第八篇文章 由于本人工作原因 中断了一段时间 接下来的一段时间里 将陆续更新本系列的其他文章 回归Spring Boot技术体系 记录本人学习和使用Gradle构建spring Boot工程的过程
  • C/C++蓝桥杯三升序列

    本题答案可能是错的 我算出的是180414 和很多博主的答案不一样 我也不太懂哪里有问题 大家可以探讨一下 题目描述 对于一个字母矩阵 我们称矩阵中的一个三升序列是指在矩阵中找到三个字母 它们在同一行 同一列 或者在同一45 度的斜线上 这
  • OpenFlow流表_时间因素

    目标 现有拓扑结构如下的网络结构 s1 s4为交换机 h1 h9为主机 现欲让h1和h2白天ping不通 晚上ping的通 拓扑结构 s1
  • es6查找指定字符下标,并把第一个字符前面的内容删掉

    前言 用es6实现查找指定字符下标 并把第一个字符前面的内容删掉 原字符串 abc 123 bcd 444 目标字符 123 bcd 444 实现方法 let abc abc 123 bcd 444 let mmm abc indexOf
  • springcloud与springboot版本列表查看及依赖关系查看

    一 版本规则说明 1 springboot版本 Spring Boot 2 2 5 RELEASE表示主版本 次版本 增量版本 Bug修复 主要 版本中的第一个数字 2 和 3 是 Python 的著名 主要版本 主要部分是基于日历的最常见
  • (华硕)电脑第一次开机蓝屏,二次开机启动

    华硕主板 第一次开机蓝屏 二次开机启动 使用了无缓存的M 2 SSD 2个通道的SSD 修改开机模式 改成兼容模式启动
  • 【H5】阻止H5页面播放视频默认全屏

    老是看到有人找不到阻止视频默认全屏的问题 看到别人发的帖子不是隐藏video标签使用canvas绘制视频就是使用插件来禁止视频默认全屏的问题 其实没有那么麻烦的只需要设置一下属性就可以了 x5 playsinline true 安卓需要设置
  • gRPC-Java实现各种服务类型

    gRPC实现 借助gRPC 我们可以在一个 proto文件中定义一次服务 并以gRPC支持的任何语言生成客户端和服务器 而这又可以在从大型数据中心内的服务器到电脑的各种环境中运行 gRPC为您处理不同的语言和环境 还获得了使用协议缓冲区的所
  • 设计模式-Visitor模式(访问者模式)

    什么是访问者模式 另一个例子 双重分发 开闭原则 对扩展开放 对修改关闭 优缺点 什么是访问者模式 将数据结构与处理分离开来 比如一个人 他有自己的名字 他可以吃东西 这里的 人 就是一个数据结构 名字是属性 吃东西是行为方法 处理 pub