当前位置: 首页 > news >正文

2024050901-重学 Java 设计模式《实战访问者模式》

重学 Java 设计模式:实战访问者模式「模拟家长与校长,对学生和老师的不同视角信息的访问场景」

一、前言

能力,是你前行的最大保障

年龄会不断的增长,但是什么才能让你不慌张。一定是能力,即使是在一个看似还很安稳的工作中也是一样,只有拥有能留下的本事跳出去的能力,你才会是安稳的。而能力的提升是不断突破自己的未知也就是拓展宽度,以及在专业领域建设个人影响力也就是深度。如果日复日365天,天天搬砖,一切都没有变化的重复只能让手上增长点老茧,岁月又叹人生苦短。

站得高看的远吗?

站得高确实能看得远,也能给自己更多的追求。但,站的高了,原本看的清的东西就变得看不清了。视角和重点的不同,会让我们有很多不同的选择,而脚踏实地是给自己奠定能攀升起来的基石,当真的可以四平八稳的走向山头的时候,才是适合看到更远的时候。

数学好才能学编码吗

往往很多时候学编程的初学者都会问数学不好能学会吗?其实可以想想那为什么数学不好呢?在这条没学好的路上,你为它们付出了多少时间呢?如果一件事情你敢做到和写自己名字一样熟悉,还真的有难的东西吗。从大学到毕业能写出40万行代码的,还能愁找不到工作吗,日积月累,每一天并没有多难。难的你想用最后一个月的时间学完人家四年努力的成绩的。学习,要趁早。

二、开发环境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三个,可以通过关注公众号bugstack虫洞栈,回复源码下载获取(打开获取的链接,找到序号18)
工程描述
itstack-demo-design-22-00场景模拟工程;模拟学生和老师信息不同视角访问

三、访问者模式介绍

访问者模式,图片来自 refactoringguru.cn

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

美女吃冰激凌

说白了访问者模式的核心在于同一个事物不同视角下的访问信息不同,比如一个美女手里拿个冰激凌。小朋友会注意冰激凌,大朋友会找自己喜欢的地方观测敌情。

四、案例场景模拟

场景模拟;校园中的学生和老师对于不同用户的访问视角

在本案例中我们模拟校园中的学生和老师对于不同用户的访问视角

这个案例场景我们模拟校园中有学生和老师两种身份的用户,那么对于家长和校长关心的角度来看,他们的视角是不同的。家长更关心孩子的成绩和老师的能力,校长更关心老师所在班级学生的人数和升学率{此处模拟的}。

那么这样学生老师就是一个固定信息的内容,而想让不同视角的用户获取关心的信息,就比较适合使用观察者模式来实现,从而让实体与业务解耦,增强扩展性。但观察者模式的整体类结构相对复杂,需要梳理清楚再开发

五、访问者模式搭建工程

访问者模式的类结构相对其他设计模式来说比较复杂,但这样的设计模式在我看来更加烧气有魅力,它能阔开你对代码结构的新认知,用这样思维不断的建设出更好的代码架构。

关于这个案例的核心逻辑实现,有以下几点;

  1. 建立用户抽象类和抽象访问方法,再由不同的用户实现;老师和学生。
  2. 建立访问者接口,用于不同人员的访问操作;校长和家长。
  3. 最终是对数据的看板建设,用于实现不同视角的访问结果输出。

1. 工程结构

itstack-demo-design-22-00
└── src├── main│   └── java│       └── org.itstack.demo.design│           ├── user│           │	  ├── impl│           │	  │     ├── Student.java│           │	  │     └── Teacher.java│           │	  └── User.java   │           ├── visitor│           │	  ├── impl│           │	  │     ├── Parent.java│           │	  │     └── Principal.java│           │	  └── Visitor.java│           └──  DataView.java└── test└── java└── org.itstack.demo.design.test└── ApiTest.java

访问者模式模型结构

访问者模式模型结构

以上是视图展示了代码的核心结构,主要包括不同视角下的不同用户访问模型。

在这里有一个关键的点非常重要,也就是整套设计模式的核心组成部分;visitor.visit(this),这个方法在每一个用户实现类里,包括;StudentTeacher。在以下的实现中可以重点关注。

2. 代码实现

2.1 定义用户抽象类
// 基础用户信息
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;}// 核心访问方法public abstract void accept(Visitor visitor);}
  • 基础信息包括;姓名、身份、班级,也可以是一个业务用户属性类。
  • 定义抽象核心方法,abstract void accept(Visitor visitor),这个方法是为了让后续的用户具体实现者都能提供出一个访问方法,共外部使用。
2.2 实现用户信息(老师和学生)

老师类

public class Teacher extends User {public Teacher(String name, String identity, String clazz) {super(name, identity, clazz);}public void accept(Visitor visitor) {visitor.visit(this);}// 升本率public double entranceRatio() {return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();}}

学生类

public class Student extends User {public Student(String name, String identity, String clazz) {super(name, identity, clazz);}public void accept(Visitor visitor) {visitor.visit(this);}public int ranking() {return (int) (Math.random() * 100);}}
  • 这里实现了老师和学生类,都提供了父类的构造函数。
  • accept方法中,提供了本地对象的访问;visitor.visit(this),这块需要加深理解。
  • 老师和学生类又都单独提供了各自的特性方法;升本率(entranceRatio)、排名(ranking),类似这样的方法可以按照业务需求进行扩展。
2.3 定义访问数据接口
public interface Visitor {// 访问学生信息void visit(Student student);// 访问老师信息void visit(Teacher teacher);}
  • 访问的接口比较简单,相同的方法名称,不同的入参用户类型。
  • 让具体的访问者类,在实现时可以关注每一种用户类型的具体访问数据对象,例如;升学率和排名。
2.4 实现访问类型(校长和家长)

访问者;校长

public class Principal implements Visitor {private Logger logger = LoggerFactory.getLogger(Principal.class);public void visit(Student student) {logger.info("学生信息 姓名:{} 班级:{}", student.name, student.clazz);}public void visit(Teacher teacher) {logger.info("学生信息 姓名:{} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());}}

访问者;家长

public class Parent implements Visitor {private Logger logger = LoggerFactory.getLogger(Parent.class);public void visit(Student student) {logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());}public void visit(Teacher teacher) {logger.info("老师信息 姓名:{} 班级:{} 级别:{}", teacher.name, teacher.clazz, teacher.identity);}}
  • 以上是两个具体的访问者实现类,他们都有自己的视角需求。
  • 校长关注;学生的名称和班级,老师对这个班级的升学率
  • 家长关注;自己家孩子的排名,老师的班级和教学水平
2.5 数据看版
public class DataView {List<User> userList = new ArrayList<User>();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. 测试验证

3.1 编写测试类
@Test
public void test(){DataView dataView = new DataView();      logger.info("\r\n家长视角访问:");dataView.show(new Parent());     // 家长logger.info("\r\n校长视角访问:");dataView.show(new Principal());  // 校长
}
  • 从测试类可以看到,家长和校长分别是不同的访问视角。
3.2 测试结果
23:00:39.726 [main] INFO  org.itstack.demo.design.test.ApiTest - 
家长视角访问:
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:谢飞机 班级:一年一班 排名:62
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:windy 班级:一年一班 排名:51
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:大毛 班级:二年三班 排名:16
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:Shing 班级:三年四班 排名:98
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:BK 班级:一年一班 级别:特级教师
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:娜娜Goddess 班级:一年一班 级别:特级教师
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:dangdang 班级:二年三班 级别:普通教师
23:00:39.730 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:泽东 班级:三年四班 级别:实习教师
23:00:39.730 [main] INFO  org.itstack.demo.design.test.ApiTest - 
校长视角访问:
23:00:39.731 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:谢飞机 班级:一年一班
23:00:39.731 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:windy 班级:一年一班
23:00:39.731 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:大毛 班级:二年三班
23:00:39.731 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:Shing 班级:三年四班
23:00:39.733 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:BK 班级:一年一班 升学率:70.62
23:00:39.733 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:娜娜Goddess 班级:一年一班 升学率:23.15
23:00:39.734 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:dangdang 班级:二年三班 升学率:70.98
23:00:39.734 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:泽东 班级:三年四班 升学率:90.14Process finished with exit code 0
  • 通过测试结果可以看到,家长和校长的访问视角同步,数据也是差异化的。
  • 家长视角看到学生的排名;排名:62排名:51排名:16排名:98
  • 校长视角看到班级升学率;升学率:70.62升学率:23.15升学率:70.98升学率:90.14
  • 通过这样的测试结果,可以看到访问者模式的初心和结果,在适合的场景运用合适的模式,非常有利于程序开发。

六、总结

  • 从以上的业务场景中可以看到,在嵌入访问者模式后,可以让整个工程结构变得容易添加和修改。也就做到了系统服务之间的解耦,不至于为了不同类型信息的访问而增加很多多余的if判断或者类的强制转换。也就是通过这样的设计模式而让代码结构更加清晰。
  • 另外在实现的过程可能你可能也发现了,定义抽象类的时候还需要等待访问者接口的定义,这样的设计首先从实现上会让代码的组织变得有些难度。另外从设计模式原则的角度来看,违背了迪米特原则,也就是最少知道原则。因此在使用上一定要符合场景的运用,以及提取这部分设计思想的精髓。
  • 好的学习方式才好更容易接受知识,学习编程的更需要的不单单是看,而是操作。二十多种设计模式每一种都有自己的设计技巧,也可以说是巧妙之处,这些巧妙的地方往往是解决复杂难题的最佳视角。亲力亲为,才能为所欲为,为了自己的欲望而努力!

相关文章:

  • DB-Engines Ranking 2024年6月数据库排行
  • 大屏幕互动系统PHP源码 附动态背景图和配乐素材 含搭建教程
  • 【乐吾乐2D可视化组态编辑器】在线使用,快速入门
  • yolov8摔倒检测(包含数据集+训练好的模型)
  • 实现一个vue js小算法 选择不同的时间段 不交叉
  • 数智化招采平台多层级多租户能力的价值
  • php文件包含
  • three.js 第四节 - 创建顶点(索引的使用)
  • 【CS.PL】Lua 编程之道: 基础语法和数据类型 - 进度16%
  • HBase数据库面试知识点:第一部分 - 基础概念与特点(持续更新中)
  • 电商API接口的采集实例:京东商品详情API接口的采集过程和步骤分析
  • 二叉树的算法题目
  • SolidWorks官方授权代理商亿达四方带您解读最新SW版本特性
  • Java Opencv识别图片上的虫子
  • [汇总] CentOS中查询端口终止进程的指令
  • Google 是如何开发 Web 框架的
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • Angular Elements 及其运作原理
  • codis proxy处理流程
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • flutter的key在widget list的作用以及必要性
  • PHP CLI应用的调试原理
  • Redis在Web项目中的应用与实践
  • 程序员该如何有效的找工作?
  • 大型网站性能监测、分析与优化常见问题QA
  • 简析gRPC client 连接管理
  • 少走弯路,给Java 1~5 年程序员的建议
  • 算法---两个栈实现一个队列
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 鱼骨图 - 如何绘制?
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • NLPIR智能语义技术让大数据挖掘更简单
  • 数据可视化之下发图实践
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • #C++ 智能指针 std::unique_ptr 、std::shared_ptr 和 std::weak_ptr
  • #Java第九次作业--输入输出流和文件操作
  • (MTK)java文件添加简单接口并配置相应的SELinux avc 权限笔记2
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (二)springcloud实战之config配置中心
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转载)从 Java 代码到 Java 堆
  • .config、Kconfig、***_defconfig之间的关系和工作原理
  • .net 程序发生了一个不可捕获的异常
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .NET与 java通用的3DES加密解密方法
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d