Java设计模式:四、行为型模式-10:访问者模式

一、定义:访问者模式

请添加图片描述

  • 访问者模式:核心在于同一个事物不同视角下的访问信息不同。
    • 在一个稳定的数据结构下,例如用户信息、雇员信息等,增加易变的业务访问逻辑。
    • 为了增强扩展性,将两部分的业务解耦的一种设计模式。

二、模拟场景:模板模式

请添加图片描述

  • 模拟校园中的学生和老师对于不同用户的访问视角。
  • 在这个案例场景我们模拟校园中由学生和老师两种身份的用户,那么对于家长和校长关心的角度来看,他们的视角是不同的。
    • 家长更关心孩子的成绩和老师的能力,校长更关心老师所在班级学生的人数和升学率。
  • 那么这样 学生老师 就是一个固定信息的内容,而想让不同视角的用户获取关心的信息,就比较适合使用访问者模式来实现,从而让实体与业务解耦,增强扩展性。
    • 但访问者模式的整体类结构相对复杂,需要梳理清除再开发

三、改善代码:模访者模式

💡 访问者模式的类结构相对其他设计模式来说比较复杂,但这样的设计模式能阔开你对代码结构的新认知,用这样的思维不断的建设处更好的代码架构。

  • 这个案例的核心逻辑:
    • 建立用户抽象类和抽象访问方法,再由不同的用户实现:老师和学生。
    • 建立访问者接口,用于不同人员的访问操作:家长和校长。
    • 最终是对数据的看板建设,用于实现不同视角的访问结果输出。

3.0 引入依赖

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!-- LOGGING begin --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.5</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.9</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>
</dependencies>

3.1 工程结构

design-step-22
|——src|——main|--java|--com.lino.design|--user|		|--impl|		|		|--Student.java|		|		|--Teacher.java|		|--User.java|--visitor|		|--impl|		|		|--Parent.java|		|		|--Principal.java|		|--Visitor.java|-DataView.java|--test|--com.lino.design.test|-ApiTest.java

3.2 访问者模式结构图

请添加图片描述

  • 上面的视图展示了代码的核心结构,主要包括不同视角下的不同用户访问模型。
  • 访问者模式的核心组成部分:visitor.visit(this) ,这个方法在每一个用户实现类里,包括:StudentTeacher

3.3 用户抽象类和实现

3.3.1 定义用户抽象类

User.java

package com.lino.design.user;import com.lino.design.visitor.Visitor;/*** @description: 用户类*/
public abstract class User {/*** 姓名*/public String name;/*** 身份:重点班、普通班 | 特级教师、普通教师、实习教师*/public String identity;/*** 班级*/public String clazz;public User(String name, String identity, String clazz) {this.name = name;this.identity = identity;this.clazz = clazz;}/*** 核心访问方法** @param visitor 访问者*/public abstract void accept(Visitor visitor);
}
  • 基本信息包括:姓名、身份、班级,也可以是一个业务用户属性类。
  • 定义核心抽象方法:abstract void accept(Visitor visitor) ,这个方法是为了让后续的用户具体实现者都能提供出一个访问方法,供外部使用。

3.3.2 实现用户信息-学生类

Student.java

package com.lino.design.user.impl;import com.lino.design.user.User;
import com.lino.design.visitor.Visitor;
import java.util.Random;/*** @description: 学生类*/
public class Student extends User {public Student(String name, String identity, String clazz) {super(name, identity, clazz);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}/*** 排名*/public int ranking() {return (int) (Math.random() * 100);}/*** 数量*/public int count() {return 105 - new Random().nextInt(10);}
}

3.3.3 实现用户信息-老师类

Teacher.java

package com.lino.design.user.impl;import com.lino.design.user.User;
import com.lino.design.visitor.Visitor;
import java.math.BigDecimal;
import java.util.Random;/*** @description: 老师类*/
public class Teacher extends User {public Teacher(String name, String identity, String clazz) {super(name, identity, clazz);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}/*** 升本率*/public double entranceRatio() {return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();}
}
  • 这里实现了老师类和学生类,都提供了父类的构造函数。
    • accept 方法中,提供了本地对象的访问。visitor.visit(this);
  • 老师和学生类又都单独提供了各自的特性方法:升学率(entranceRatio)、排名(ranking),类似这样的方法可以按照业务需求进行扩展。

3.4 访问者接口及其实现

3.4.1 定义访问数据接口

Visitor.java

package com.lino.design.visitor;import com.lino.design.user.impl.Student;
import com.lino.design.user.impl.Teacher;/*** @description: 访问者接口*/
public interface Visitor {/*** 访问学生信息** @param student 学生类*/void visit(Student student);/*** 访问老师信息** @param teacher 老师类*/void visit(Teacher teacher);
}
  • 访问接口比较简单,相同的方法名称,不同的入参用户类型。
  • 让具体的访问者类,在实现时可以关注每一种用户类型的具体访问数据对象,例如:升学率和排名

3.4.2 访问者实现类-家长类

Parent.java

package com.lino.design.visitor.impl;import com.lino.design.user.impl.Student;
import com.lino.design.user.impl.Teacher;
import com.lino.design.visitor.Visitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 家长类*/
public class Parent implements Visitor {private Logger logger = LoggerFactory.getLogger(Parent.class);@Overridepublic void visit(Student student) {logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());}@Overridepublic void visit(Teacher teacher) {logger.info("老师信息 姓名:{} 班级:{} 级别:{}", teacher.name, teacher.clazz, teacher.identity);}
}
  • 家长关注:自己家孩子的排名,老师的班级和教学水平。

3.4.3 访问者实现类-校长类

Principal.java

package com.lino.design.visitor.impl;import com.lino.design.user.impl.Student;
import com.lino.design.user.impl.Teacher;
import com.lino.design.visitor.Visitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 校长类*/
public class Principal implements Visitor {private Logger logger = LoggerFactory.getLogger(Principal.class);@Overridepublic void visit(Student student) {logger.info("学生信息 班级:{} 人数:{}", student.clazz, student.count());}@Overridepublic void visit(Teacher teacher) {logger.info("老师信息 姓名:{} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());}
}
  • 校长关注:学生的名称和班级,老师对这个班级的升学率。

3.5 数据看板

DataView.java

package com.lino.design;import com.lino.design.user.User;
import com.lino.design.user.impl.Student;
import com.lino.design.user.impl.Teacher;
import com.lino.design.visitor.Visitor;
import java.util.ArrayList;
import java.util.List;/*** @description: 数据看板*/
public class DataView {List<User> userList = new ArrayList<>();public DataView() {userList.add(new Student("谢飞机", "重点班", "一年一班"));userList.add(new Student("windy", "重点班", "一年一班"));userList.add(new Student("大毛", "普通班", "二年三班"));userList.add(new Student("Shing", "普通班", "三年四班"));userList.add(new Teacher("BK", "特级教师", "一年一班"));userList.add(new Teacher("娜娜Goddess", "特级教师", "一年一班"));userList.add(new Teacher("dangdang", "普通教师", "二年三班"));userList.add(new Teacher("泽东", "实习教师", "三年四班"));}public void show(Visitor visitor) {for (User user : userList) {user.accept(visitor);}}
}
  • 首先在这个类中初始化了基本的数据,学生和老师的信息。
  • 并提供了一个展示类,通过传入不同的 访问者(家长、校长) 而差异化的打印信息。

3.6 单元测试

ApiTest.java

package com.lino.design.test;import com.lino.design.DataView;
import com.lino.design.visitor.impl.Parent;
import com.lino.design.visitor.impl.Principal;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test_show() {DataView dataView = new DataView();logger.info("\r\n家长视角访问:");dataView.show(new Parent());logger.info("\r\n校长视角访问:");dataView.show(new Principal());}
}
  • 测试类提供了三个商品连接,也可以是其他商品的连接。
  • 爬取的成功模拟爬取京东商品,可以替换为其他商品服务。new JDNetMallnew DangDangNetMallnew TaoBaoNetMall

测试结果

14:14:27.268 [main] INFO  com.lino.design.test.ApiTest - 
家长视角访问:
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 学生信息 姓名:谢飞机 班级:一年一班 排名:47
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 学生信息 姓名:windy 班级:一年一班 排名:26
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 学生信息 姓名:大毛 班级:二年三班 排名:46
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 学生信息 姓名:Shing 班级:三年四班 排名:2
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 老师信息 姓名:BK 班级:一年一班 级别:特级教师
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 老师信息 姓名:娜娜Goddess 班级:一年一班 级别:特级教师
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 老师信息 姓名:dangdang 班级:二年三班 级别:普通教师
14:14:27.273 [main] INFO  com.lino.design.visitor.impl.Parent - 老师信息 姓名:泽东 班级:三年四班 级别:实习教师
14:14:27.273 [main] INFO  com.lino.design.test.ApiTest - 
校长视角访问:
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 学生信息 班级:一年一班 人数:102
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 学生信息 班级:一年一班 人数:103
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 学生信息 班级:二年三班 人数:98
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 学生信息 班级:三年四班 人数:99
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 老师信息 姓名:BK 班级:一年一班 升学率:45.81
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 老师信息 姓名:娜娜Goddess 班级:一年一班 升学率:27.52
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 老师信息 姓名:dangdang 班级:二年三班 升学率:51.94
14:14:27.273 [main] INFO  c.lino.design.visitor.impl.Principal - 老师信息 姓名:泽东 班级:三年四班 升学率:37.5
  • 通过测试结果可以看到,家长和校长的访问视角同步,数据也是差异化的。
  • 家长视角看到学生的排名:排名:47排名:26排名:46排名:2
  • 校长视角看到班级升学率:升学率:45.81升学率:27.52升学率:51.94升学率:37.5
  • 通过这样的测试结果,可以看到访问者模式的初心和结果,在适合的场景运用合适的模式,非常有利于程序开发。

四、总结:访问者模式

  • 通过上面的业务场景可以看到,在嵌入访问者模式后,可以让整个工程结构变得容易添加和修改。
    • 也就做到了系统之间的解耦,不至于为了不同类型信息的访问而增加很多多余的 if 判断或者类的强制转换。
    • 也就是通过这样的设计模式而让代码结构更加清晰。
  • 另外在实现的过程可能发现,定义抽象类的时候还需要等待访问者接口的定义
    • 这样的设计首先从实现上会让代码的组织变得有些难度。
    • 另外从设计模式原则的角度来看,违背了迪米特原则,也就是最少知道原则。
    • 因此在使用上一定要符合场景的运用,以及提取这部分设计思想的精髓。

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

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

相关文章

基于springboot跟redis实现的排行榜功能(实战)

概述 前段时间&#xff0c;做了一个世界杯竞猜积分排行榜。对世界杯64场球赛胜负平进行猜测&#xff0c;猜对1分&#xff0c;错误0分&#xff0c;一人一场只能猜一次。 1.展示前一百名列表。 2.展示个人排名(如&#xff1a;张三&#xff0c;您当前的排名106579)。 一.redis so…

利用 AI 赋能云安全,亚马逊云科技的安全技术创新服务不断赋能开发者

文章分享自亚马逊云科技 Community Builder&#xff1a;李少奕 2023年6月14日&#xff0c;一年一度的亚马逊云科技 re:Inforce 全球大会在美国安纳海姆落下了帷幕。re:Inforce 是亚马逊云科技全球最大的盛会之一&#xff0c;汇集了来自全球各地的安全专家&#xff0c;共同学习、…

11.Redis数据库管理命令

Redis数据库管理命令 数据库管理selectdbsizeflushall / flushdb 数据库管理 redis 中的 database 是现成的&#xff0c;咱们用户不能创建新的数据库&#xff0c;也不能删除已有的数据库~ 默认 redis 给我们提供了 16 个数据库&#xff0c;名字为 数字0 到数字15 这16个数据库…

VS2019编译curl库

下载&#xff1a; curl-7.61.0.tar.gz 编译&#xff1a; 解压到一个文件下&#xff0c;然后右键以管理员权限运行buildconf.bat 编译x64的库使用的是x64 Native Tools Command Prompt for VS 2019 本机工具命令提示&#xff0c;如果想编译x86的库&#xff0c;可以选择x86 Nat…

纯 CSS 开关切换按钮

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>纯 CSS 开关切换按钮</title><style>html {font-size: 62.5%;}body {background-color: #1848a0;}.wrapper {position: absolute;left: …

Windows右键添加用 VSCODE 打开

1.安装VSCODE时 安装时会有个选项来添加&#xff0c;如下&#xff1a; ①将“通过code 打开“操作添加到windows资源管理器文件上下文菜单 ②将“通过code 打开”操作添加到windows资源管理器目录上下文菜单 说明&#xff1a;①②勾选上&#xff0c;可以对文件&#xff0c;目…

API管理风险:如何确保您的API安全与可靠?

API管理风险&#xff1a;如何确保您的API安全与可靠&#xff1f; 随着数字化时代的到来&#xff0c;应用程序接口&#xff08;API&#xff09;在现代软件开发中发挥着关键的作用。然而&#xff0c;API管理过程中存在着各种潜在的风险。本文将探讨如何有效地管理和缓解这些风险…

Django-跨域

一、基础概念 cors 跨域资源共享 二、跨域请求-简单请求 满足以下全部条件的请求为 简单请求 1.请求方法如下&#xff1a; GET or HEAR or POS 2.请求头仅包含如下&#xff1a; Accept、Accept-Language、Content-Language、Content-Type 3.ConTent-Type 仅支持如下三种&…

解决Ubuntu 或Debian apt-get IPv6问题:如何设置仅使用IPv4

文章目录 解决Ubuntu 或Debian apt-get IPv6问题&#xff1a;如何设置仅使用IPv4 解决Ubuntu 或Debian apt-get IPv6问题&#xff1a;如何设置仅使用IPv4 背景&#xff1a; 在Ubuntu 22.04(包括 20.04 18.04 等版本) 或 Debian (10、11、12)系统中&#xff0c;当你使用apt up…

jmeter 线程组

在jmeter中&#xff0c;通过指定并发数量、启动延迟时间和持续时间&#xff0c;并组织示例&#xff08;Samplers&#xff09;在多个线程之间的执行方式&#xff0c;实现模拟并发用户的行为。 添加线程组&#xff1a; 在测试计划中&#xff0c;右键点击“添加” -> “Thread…

音频——I2S 标准模式(二)

I2S 基本概念飞利浦(I2S)标准模式左(MSB)对齐标准模式右(LSB)对齐标准模式DSP 模式TDM 模式 文章目录 I2S format时序图逻辑分析仪抓包 I2S format 飞利浦 (I2S) 标准模式 数据在跟随 LRCLK 传输的 BCLK 的第二个上升沿时传输 MSB&#xff0c;其他位一直到 LSB 按顺序传传输依…

考前冲刺上岸浙工商MBA的备考经验分享

2023年对于许多人来说都是不平凡的一年&#xff0c;历经三年的抗争&#xff0c;我们终于成功结束了疫情。而我也很幸运的被浙工商MBA项目录取&#xff0c;即将开始全新的学习生活。身为一名已在职工作6年的人&#xff0c;能够重回校园真是一种特别令人激动的体验。今天&#xf…

2023.9.2 关于 JVM 垃圾回收机制(GC)

目录 为什么要有垃圾回收机制? STW&#xff08;Stop The World&#xff09;问题 垃圾回收机制主要回收哪个内存区域? 垃圾对象判断算法 引用计数算法 可达性分析算法 垃圾对象回收算法 标记清除算法 复制算法 标记整理算法 分代算法 为什么要有垃圾回收机制? 自动…

每日一题(链表中倒数第k个节点)

每日一题&#xff08;链表中倒数第k个节点&#xff09; 链表中倒数第k个结点_牛客网 (nowcoder.com) 思路: 如下图所示&#xff1a;此题仍然定义两个指针&#xff0c;fast指针和slow指针&#xff0c;假设链表的长度是5&#xff0c;k是3&#xff0c;那么倒数第3个节点就是值为…

【Jenkins打包服务,Dockerfile报错:manifest for java : 8 not fourd】

1、问题描述 Jenkins打包服务运行dockerfile里的FROM java:8报错manifest for java : 8 not fourd Caused by: com.spotify. docker.client.exceptions.DockerException: manifest for java:8 not found2、解决方法 在网上查找许多方法后得出这是由于Docker官方已经弃用java…

Java自定义异常

Java标准库定义的常用异常包括&#xff1a; 当我们在代码中需要抛出异常时&#xff0c;尽量使用JDK已定义的异常类型。例如&#xff0c;参数检查不合法&#xff0c;应该抛出IllegalArgumentException&#xff1a; static void process1(int age) {if (age < 0) {throw new…

7.2 项目2 学生通讯录管理:文本文件增删改查(C 版本)(自顶向下设计+断点调试) (A)

C自学精简教程 目录(必读) 该作业是 作业 学生通讯录管理&#xff1a;文本文件增删改查&#xff08;C版本&#xff09; 的C 语言版本。 具体的作业题目描述&#xff0c;要求&#xff0c;可以参考 学生通讯录管理&#xff1a;文本文件增删改查&#xff08;C版本&#xff09;。…

CentOS配置Java环境报错-bash: /usr/local/jdk1.8.0_381/bin/java: 无法执行二进制文件

CentOS配置Java环境后执行java -version时报错&#xff1a; -bash: /usr/local/jdk1.8.0_381/bin/java: 无法执行二进制文件原因是所使用的jdk的版本和Linux内核架构匹配不上 使用以下命令查看Linux架构&#xff1a; [rootlocalhost ~]# cat /proc/version Linux version 3.1…

当众讲话紧张不敢说话怎么办?

当在公众场合讲话时&#xff0c;紧张和不敢说话是很常见的问题。这种情况下&#xff0c;以下是一些帮助你克服紧张并提高自信的技巧和建议&#xff1a; 1. 准备充分&#xff1a;充分的准备是克服紧张的关键。在讲话前&#xff0c;确保你对主题有足够的了解&#xff0c;并进行适…

stable diffusion实践操作-webUI教程

系列文章目录 stable diffusion实践操作 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、SD webUI是什么&#xff1f;二、详细教程1. 插件安装1.1. 提示词插件安装和使用 2. xyz 图表使用2.1…