java设计模式学习之【访问者模式】

文章目录

  • 引言
  • 访问者模式简介
    • 定义与用途
    • 实现方式
  • 使用场景
  • 优势与劣势
  • 在Spring框架中的应用
  • 电脑示例
  • 代码地址

引言

设想你是一个艺术馆的管理员,艺术馆里有各种各样的艺术品。每当有游客来访时,根据他们的兴趣,他们可能只想看画、雕塑或特定的展览。在这里,每位游客都有不同的“访问”行为,而艺术馆提供了他们所能“访问”的物品。在软件开发中,我们经常遇到需要对一个复杂的对象结构(如一个元素集合)执行不同操作的情况,而不希望修改对象的类。访问者模式提供了一种将操作逻辑与对象结构分离的方法,使得我们可以在不修改已有程序结构的情况下,向程序添加新的操作。

访问者模式简介

定义与用途

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不改变各元素类的前提下,定义作用于这些元素的新操作。它使用一种访问者类,改变元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者变化而变化。

实现方式

实现访问者模式通常涉及以下几个关键组件:

  • 访问者(Visitor)接口: 定义了对每一种元素(Element)类访问的操作。
  • 具体访问者(Concrete Visitor): 实现了访问者接口的类,定义了对每一种元素的具体访问行为。
  • 元素(Element)接口: 定义了一个接受访问者(accept)的方法。
  • 具体元素(Concrete Element): 实现了元素接口,其方法 accept 通常将自己作为参数传递给访问者的访问方法。

使用场景

访问者模式适用于以下场景:

  • 当一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象执行一些依赖于其具体类的操作。
  • 当需要对一个复杂的对象结构执行很多不相关的操作,而你不想在这些操作污染这些对象的类时。访问者可以将相关的操作组织在一个类中。
  • 当多个对象间存在一种“双重分派”关系时。即想根据两个对象的实际类型决定执行的操作。

例如:

  • 文档解析器: 文档结构中包含多种类型的元素,如文本、图片和表格。访问者模式可以用来实现文档的渲染、格式校验或者内容提取等操作。
  • 保险评估工具: 对不同类型的保险单执行风险评估和报价。每种保险单都有不同的风险评估算法,而所有算法都可以通过访问者模式来实现。
  • 公园管理系统: 一个公园包含多种类型的景点,如游乐场、植物园和博物馆。访问者模式可以用来实现不同访客对不同景点的不同访问行为,例如儿童、成人和园艺师。

优势与劣势

  • 优势
    • 增加新的操作很容易,只需添加一个新的访问者即可。
    • 将相关的操作集中到一个访问者中,而不是分散在多个元素类中。
  • 劣势
    • 增加新的元素类变得困难,每增加一个新的元素类,每一个访问者都可能需要修改。
    • 具体元素对访问者公开细节,违反了封装原则。

在Spring框架中的应用

在Spring框架中,BeanDefinitionVisitor 是访问者模式的一个实际应用例子,它用于访问和修改 BeanDefinition 对象。BeanDefinition 对象包含了Spring容器中bean的配置信息,如类名、属性值和构造函数参数。作用与用途:
BeanDefinitionVisitor 主要用于在Spring容器启动过程中修改已经加载的 BeanDefinition。它通过访问者模式允许开发者编写自定义逻辑来检查和修改 BeanDefinition,而不需要修改 BeanDefinition 的源代码。如何工作:
访问Bean定义: BeanDefinitionVisitor 遍历Bean定义中的属性值和构造函数参数等信息。
修改Bean定义: 它可以根据需要修改这些信息,例如解析占位符、应用属性编辑器或者更改属性值。
扩展性和灵活性: 开发者可以扩展 BeanDefinitionVisitor 类并覆盖相应的方法来实现自定义访问和修改逻辑。
使用场景:
属性占位符替换: PropertyPlaceholderConfigurer 是Spring中一个常见的使用 BeanDefinitionVisitor 的例子。它在容器启动时解析并替换属性文件中的占位符。
属性编辑器应用: 自定义 BeanDefinitionVisitor 可以用于在bean属性赋值之前应用自定义属性编辑器,进行类型转换或者其他预处理操作。
条件配置: 通过检查 BeanDefinition 的详细信息,BeanDefinitionVisitor 可以实现条件配置,根据不同的环境或条件选择性地修改或激活bean。
实现自定义BeanDefinitionVisitor:
要实现自定义的 BeanDefinitionVisitor,可以继承 BeanDefinitionVisitor 类,并重写 visitXXX 方法来实现特定的访问和修改逻辑。然后在容器启动过程中,或者作为 BeanFactoryPostProcessor 的一部分应用这个访问者到需要的 BeanDefinition 上。

电脑示例

在这里插入图片描述
步骤 1:定义代表元素的接口

public interface ComputerPart {public void accept(ComputerPartVisitor computerPartVisitor);
}

ComputerPart 接口定义了 accept 方法,所有具体的计算机部件类将实现这个接口,以允许访问者访问它们。

步骤 2:创建扩展上述类的具体类

public class Keyboard implements ComputerPart {@Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}

Keyboard 是一个具体的 ComputerPart,实现了 accept 方法,允许访问者访问键盘。

public class Monitor implements ComputerPart {@Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}

Monitor 是另一个具体的 ComputerPart,实现了 accept 方法,允许访问者访问显示器。

public class Mouse implements ComputerPart {@Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}

Mouse 是另一个具体的 ComputerPart,实现了 accept 方法,允许访问者访问鼠标。

public class Computer implements ComputerPart {ComputerPart[] parts;public Computer(){parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};		} @Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {for (ComputerPart part : parts) {part.accept(computerPartVisitor);}computerPartVisitor.visit(this);}
}

Computer 是一个复合的 ComputerPart,它包含其他部件。它的 accept 方法首先让访问者访问它的每个部分,然后访问计算机本身。

步骤 3:定义代表访问者的接口

public interface ComputerPartVisitor {public void visit(Computer computer);public void visit(Mouse mouse);public void visit(Keyboard keyboard);public void visit(Monitor monitor);
}

ComputerPartVisitor 接口定义了对每种类型的 ComputerPart 的访问操作。

步骤 4:创建实现上述类的具体访问者

public class ComputerPartDisplayVisitor implements ComputerPartVisitor {@Overridepublic void visit(Computer computer) {System.out.println("展示电脑。");}@Overridepublic void visit(Mouse mouse) {System.out.println("展示鼠标。");}@Overridepublic void visit(Keyboard keyboard) {System.out.println("展示键盘。");}@Overridepublic void visit(Monitor monitor) {System.out.println("展示显示器。");}
}

ComputerPartDisplayVisitor 是一个具体的访问者,它定义了如何展示每个部件。

步骤 5:使用 ComputerPartDisplayVisitor 来展示计算机的部件

public class VisitorPatternDemo {public static void main(String[] args) {ComputerPart computer = new Computer();computer.accept(new ComputerPartDisplayVisitor());}
}

在这里插入图片描述

在这个演示类中,我们创建了一个 Computer 对象并用 ComputerPartDisplayVisitor 来展示它的各个部件。

这个示例演示了访问者模式如何使得你可以定义新的操作而无需改变其操作的元素的类。通过这种方式,ComputerPartDisplayVisitor 可以添加新的展示行为而无需修改 ComputerPart 接口或其具体类的任何代码。这使得在不改变已有代码结构的前提下增加新的操作变得容易,从而提高了代码的可维护性和可扩展性。

代码地址

23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/307889.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何通过易舟云财务软件,进行结转结账?

如何通过易舟云财务软件,进行结转结账? 前言财务软件操作步骤1、期末检查2、计提所得税3、结转损益4、断号检查5、结账 注意事项 前言 财务软件的结转结账功能是指在财务管理过程中,将上一期的未结转的财务数据进行处理,包括将未…

MyBatis-config.xml配置文件

1、基本介绍: mybatis的核心配置文件(mybatis-config.xml),比如配置jdbc连接信息,注册mapper等等,我们需要对这个配置文件有详细的了解。 官网地址有详细介绍 mybatis – MyBatis 3 | 配置 2、properties属性 在通常的情况下&am…

右键添加 idea 打开功能

1.开始运行regedit 2.找到: HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell _3.开始设置 一、右键shell目录新建项Idea二、右键Idea新建command三、选择Idea 右侧空白出新建字符串 名字为Icon 值填入idea的运行程序地址 四、选择command 默认项填入idea的运行程序地址…

腾讯经典面试题-如何做一个迷你版的微信抢红包呢?

👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术🔥如果感觉博主的文章还不错的…

[Angular] 笔记 17:提交表单 - ngSubmit

Submitting Forms (ngSubmit) 表单的一般完整写法: 如果表单验证失败,必须 disable 提交按钮,阻止用户提交不合法的数据。 提交表单后,与表单对应的 json 数据 post 到后端: {"id":1,"name":…

单片机开发从小工到专家

有道无术,术尚可求;有术无道,止于术 背景 向单片机嵌入式开发小伙伴推荐了几本书,阅读量破10 1. 适用范围 2. 书籍推荐 书籍推荐 3. 大师介绍 大师介绍 4. 大师书籍编写逻辑 25年大师出版的关于:嵌入式单片…

《我与地坛》当时只道是寻常;只是当时已惘然

《我与地坛》当时只道是寻常;只是当时已惘然 史铁生(1951/1/4-2010/12/31),作家,散文家,代表作有《我与地坛》《命若琴弦》《奶奶的星星》等。 文章目录 《我与地坛》当时只道是寻常;只是当时已…

以社区为基石,IvorySQL逐步成为中国基础软件开源数据库产业重要一员

编者按:开源数据库技术,作为软件开发领域的一大趋势,正逐渐改变整个软件产业的面貌。在这个充满活力的领域中,瀚高股份的IvorySQL凭借其社区活跃度和影响力,已经成为中国基础软件开源数据库产业的重要一员。随着《2023…

Chrome插件精选 — 前端工具

Chrome实现同一功能的插件往往有多款产品,逐一去安装试用耗时又费力,在此为某一类型插件挑选出比较好用的一款或几款,尽量满足界面精致、功能齐全、设置选项丰富的使用要求,便于节省一个个去尝试的时间和精力。 1. FeHelper(前端助…

2023-12-29 工作心得补充 适时抽取方法,让代码变简洁

1 JSONObject 实际上是个map 2 数据库实际上也是map 只不过map 是竖着写,数据库横着写. 3 像 用户名 密码 这种后续可能随时会改的,不要写死在代码里,都写成nacos参数。 4 方法的抽取 让代码变得简洁 可读性很高。这是方法抽取的秘诀。写文…

利用Pandas进行高效网络数据获取

利用Pandas进行高效网络数据获取 背景: ​ 最近看到一篇关于使用Pandas模块进行爬虫的文章,觉得很有趣,这里为大家详细说明。 基础铺垫: ​ pd.read_html pandas 库中的一个函数,用于从 HTML 页面中读取表格数据并…

jQuery实现layer.open中按钮倒计时读秒可用的协议阅读场景

今日遇到一个系统注册页网站 条款签接受流程改动的需求,往日多是使用他人网站注册登录,看见相关协议的授权设计大同小样,觉得挺有意思,这次遇到了需要我来实现这个功能,但是用习惯了vue的封装,这次是依靠jQ…