作者:小傅哥

博客:https://bugstack.cn - 原创系列专题文章

沉淀、分享、成长,让自己和他人都能有所收获!

一、前言

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

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

站得高看的远吗?

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

数学好才能学编码吗

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

二、开发环境

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

三、访问者模式介绍

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

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

四、案例场景模拟

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

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

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

五、访问者模式搭建工程

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

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

  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.14 Process finished with exit code 0
  • 通过测试结果可以看到,家长和校长的访问视角同步,数据也是差异化的。
  • 家长视角看到学生的排名;排名:62排名:51排名:16排名:98
  • 校长视角看到班级升学率;升学率:70.62升学率:23.15升学率:70.98升学率:90.14
  • 通过这样的测试结果,可以看到访问者模式的初心和结果,在适合的场景运用合适的模式,非常有利于程序开发。

六、总结

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

七、推荐阅读

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

  1. 重学 Java 设计模式:实战责任链模式「模拟618电商大促期间,项目上线流程多级负责人审批场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 场地和场景的重要性 射击

  2. 重学 Java 设计模式:实战迭代器模式「模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相信相信的力量! 从懵懂的少年,到拿起键盘,可以写一个Hell ...

  3. 重学 Java 设计模式:实战外观模式「基于SpringBoot开发门面模式中间件,统一控制接口白名单场景」

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你感受到的容易,一定有人为你承担不容易 这句话更像是描述生活的,许许多多的磕磕绊绊总 ...

  4. 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 程序员‍‍的上下文是什么? 很多时候一大部分编程开发的人员都只是关注于功能的实现,只 ...

  5. 重学 Java 设计模式:实战代理模式「模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景」

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 难以跨越的瓶颈期,把你拿捏滴死死的! 编程开发学习过程中遇到的瓶颈期,往往是由于看不 ...

  6. 重学 Java 设计模式:实战备忘录模式「模拟互联网系统上线过程中,配置文件回滚场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 实现不了是研发的借口? 实现不了,有时候是功能复杂度较高难以实 ...

  7. 重学 Java 设计模式:实战状态模式「模拟系统营销活动,状态流程审核发布上线场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! @ 目录 一.前言 二.开发环境 三.状态模式介绍 四.案例场景模拟 1 ...

  8. 重学 Java 设计模式:实战桥接模式(多支付渠道「微信、支付宝」与多支付模式「刷脸、指纹」场景)

    作者:小傅哥 博客:https://bugstack.cn - 编写系列原创专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么你的代码那么多ifelse 同类的业务.同样的功能, ...

  9. 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...

随机推荐

  1. 【转载】图解NumPy

    目录 1. 读写文件 2. 内建向量/矩阵 3. 切片操作 4. 聚合函数 4.1. 向量 4.2. 矩阵 5. 矩阵的转置和重构 6. 常用操作API 7. 应用实例 7.1. 生成向量.矩阵 7. ...

  2. OpenSSH详解

    OpenSSH详解(思维导图) 1. SSH概述 SSH 软件架构 认证方式 2. OpenSSH 2.1 客户端程序ssh 配置文件 ssh命令 客户端免密登录 scp sftp 2.2 服务端程序 ...

  3. 迁移AndroidX

    1. 前言 AndroidX replaces the original support library APIs with packages in the androidx namespace. O ...

  4. vue事件修饰符与按钮修饰符

    事件修饰符:(当事件无需传参数时可直接写成以下形式,有参数时则为@click.stop="handleLiClick($event)") stop:阻止事件冒泡行为(子元素被点击, ...

  5. Python学习之路——pycharm的第一个项目

    Python学习之路——pycharm的第一个项目 简介: 上文中已经介绍如何安装Pycharm已经环境变量的配置.现在软件已经安装成功,现在就开始动手做第一个Python项目.第一个“Hello W ...

  6. Beta冲刺总结随笔

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 团队名称 WeChair 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺 作业正文 如下 其他参考文献 项目预期 ...

  7. RabbitMQ入门,我是动了心的

    人一辈子最值得炫耀的不应该是你的财富有多少(虽然这话说得有点违心,呵呵),而是你的学习能力.技术更新迭代的速度非常快,那作为程序员,我们就应该拥有一颗拥抱变化的心,积极地跟进. 在 RabbitMQ ...

  8. selenium3.0-selenium发展史

  9. v-model指令的学习(双向绑定)

    v-bind 只能实现数据的单项绑定,从data到view,无法实现双向绑定 v-model可以实现表单元素和model中数据的双向绑定 注意:model只能运用到表单元素中 如:input sele ...

  10. 第四模块 :微服务调用链监控CAT架构和实践

    采样率:每一个请求为都进行记录,或者100次请求为记录50次 各个开源框架都满足opentracing的标准,只要使用opentracing标准埋点的客户端,可以使用不同的客户端去展示,opentra ...