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

news2024/11/25 4:44:04

文章目录

  • 引言
  • 访问者模式简介
    • 定义与用途
    • 实现方式
  • 使用场景
  • 优势与劣势
  • 在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 {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

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

public class Monitor implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

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

public class Mouse implements ComputerPart {

   @Override
   public 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()};		
   } 

   @Override
   public 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 {

   @Override
   public void visit(Computer computer) {
      System.out.println("展示电脑。");
   }

   @Override
   public void visit(Mouse mouse) {
      System.out.println("展示鼠标。");
   }

   @Override
   public void visit(Keyboard keyboard) {
      System.out.println("展示键盘。");
   }

   @Override
   public 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.coloradmin.cn/o/1343277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

私有部署ELK,搭建自己的日志中心(六)-- 引入kafka对采集日志进行削峰填谷

一、背景 首先,要说明一点,elk日志中心,是可以缺少kafka组件的。 其次,如果是研发环境下,机器资源紧张的情况下,也是可不部署kafka。 最后,因为kafka的部署是可以独立的,所以本文将…

成都服装(服饰)行业2023年度工作总结表彰大会暨五圣荟户外大本营签约发布会

凝心聚力谋发展,稳中求进谱新篇,由成都服装(服饰)行业协会主办,成都梵圣万汇文化发展有限公司承办的成都服装(服饰)行业2023年度工作总结表彰大会暨五圣荟户外大本营签约发布会于2023年12月28日…

数据库系统(六)数据库范式 | 函数依赖,一二三范式,BCNF,属性集闭包和正则覆盖

文章目录 1 好的关系设计的特征2 函数依赖关系3 Normal Forms 规范形式3.1 一二三范式3.1.1 基本概念3.1.2 判断是否满足3NF 3.2 BCNF3.2.1 基本概念3.2.2 判断是否满足BCNF3.2.3 分解得到BCNF 4 属性集闭包和正则覆盖4.1 属性集闭包求法4.2 属性集闭包应用4.2.1 测试某个属性集…

借助 Google Play 游戏电脑版新功能,加速业务增长

作者 / Google Play 游戏总监 Arjun Dayal Google Play 游戏电脑版测试版自去年发布以来,取得了巨大的发展。Google Play 游戏电脑版现在提供 3,000 多种游戏,覆盖 120 多个国家/地区的用户,为玩家提供各种类型的游戏。我们的热门移动游戏目录…

4-链表-合并两个有序链表

这是链表的第4题,来个简单算法玩玩。力扣链接。 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2&#xff…

Kindle使用USB数据线传书封面无法显示问题

以下内容只针对USB传书(非越狱版本,越狱了有相关插件,这里不谈),不包括邮件传书。 恶心图如下: 直接把mobi/azw3/azw (epub模式不能直接拷贝,kindle无法读取)格式的电子…

99. 恢复二叉搜索树

#中序遍历,寻找插值位置并交换 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def recoverTree…

基于MINIST的手写数字体识别

一、算法简述 网络结构设计 通过创建MnistNet类,定义了包含两个卷积层和两个全连接层的深度神经网络。这个网络的设计灵感来自于经典的CNN结构,其中卷积层用于提取图像特征,而全连接层则用于将这些特征映射到最终的类别。 卷积与池化 卷…

新能源汽车高性价比电驱系统设计技术

版权声明:此资源由用户收集整理于网络,只用于交流学习,请勿用作它途。除非确实无法确认,我们都会注明作者和来源,无法确认情况我们标注来自网络。若涉及版权问题,烦请原作者联系我们,与您共同协…

【软件工程】漫谈增量过程模型:软件开发的逐步之道

🍎个人博客:个人主页 🏆个人专栏: 软件工程 ⛳️ 功不唐捐,玉汝于成 目录 前言: 正文 增量过程模型(Incremental Process Model) 主要特点和阶段: 优点&#xff1…

第五课:集成电路与摩尔定律(硬件的发展)、操作系统、内存和储存介质(存储技术的发展)、文件系统、压缩、命令行界面及屏幕与 2D 图形显示

第五课:集成电路与摩尔定律(硬件的发展)、操作系统、内存和储存介质(存储技术的发展)、文件系统、压缩、命令行界面及屏幕与 2D 图形显示 第十七章:集成电路与摩尔定律(硬件的发展)1…

爬虫工作量由小到大的思维转变---<第三十四章 Scrapy 的部署scrapyd+Gerapy>

前言: scrapy-redis没被部署,感觉讲起来很无力;因为实在编不出一个能让scrapy-redis发挥用武之地的案子;所以,索性直接先把分布式爬虫的部署问题给讲清楚!! 然后,曲线救国式地再在部署的服务器上,讲scrapy redis我感觉这样才好! 正文: 现在还有不少人在用scrapy web进行爬虫管…

Springboot启动流程-持续记录中

注:转载请携带本文链接及公众号信息 公众号:codelike 基于springboot2.6.x 源码 Springboot启动之第一篇 SpringApplication构造器 启动入口方法是new SpringApplication.run(),一切的开始都从这里 这里做了什么呢 分为初始化SpringApplication实体、执行run()…

rime中州韵 自定义词典

在使用 rime 输入法的过程, 我们往往会需要增加一些个个性化的词条,这些词条我们可能通过自定义词典的方式来管理和使用。 Custom_phrase.txt 首先,我们需要有一个词典文档,这里我们把 Custom_phrase.txt 文档作为我们的自定义文…

c语言-string.h库函数初识

目录 前言一、库函数strlen()1.1 strlen()介绍1.2 模拟实现strlen() 二、库函数strcpy()2.1 strcpy()介绍2.2 模拟实现strcpy() 三、库函数strcmp()3.1 strcmp()介绍3.3 模拟实现strcmp() 总结 前言 本篇文章介绍c语言<string.h>头文件中的库函数&#xff0c;包含strlen…

汽车电子喇叭,预计未来几年全球市场将以显着的速度增长

根据最近的市场研究报告&#xff0c;预计未来几年全球汽车电子喇叭市场将以显着的速度增长。这种增长是由对汽车高级安全系统的需求不断增加以及电动汽车 (EV) 和混合动力电动汽车 (HEV) 的日益普及所推动的。此外&#xff0c;智慧城市的发展和对车对车&#xff08;V2V&#xf…

CEC2017(Python):五种算法(HHO、RFO、OOA、PSO、GWO)求解CEC2017

一、5种算法简介 1、哈里斯鹰优化算法HHO 2、红狐优化算法RFO 3、鱼鹰优化算法OOA 4、粒子群优化算法PSO 5、灰狼优化算法GWO 二、CEC2017简介 参考文献&#xff1a; [1]Awad, N. H., Ali, M. Z., Liang, J. J., Qu, B. Y., & Suganthan, P. N. (2016). “Problem d…

Python 操作 MySQL:使用 mysql-connector-python 操作 MySQL 数据库

大家好&#xff0c;我是水滴~~ 当涉及到使用 Python 操作 MySQL 数据库时&#xff0c;mysql-connector-python 库是一个强大而常用的选择。该库提供了与 MySQL 数据库的交互功能&#xff0c;使您能够执行各种数据库操作&#xff0c;如连接数据库、执行查询和插入数据等。在本文…

余弦相似度算法

余弦相似度算法 是什么 余弦距离&#xff0c;也称为余弦相似度&#xff0c;是用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量。 余弦值越接近1&#xff0c;就表明夹角越接近0度&#xff0c;也就是两个向量越相似&#xff0c;这就叫"余弦相似性&q…

PBR纹理贴图类型详解

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 PBR 纹理是一种帮助 3D 艺术家使他们的 3D 渲染看起来更逼真的技术。…