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

news2025/2/21 21:21:04

一、定义:访问者模式

请添加图片描述

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

二、模拟场景:模板模式

请添加图片描述

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

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

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

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

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);
    }

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

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

    @Override
    public void visit(Student student) {
        logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());
    }

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

    @Override
    public void visit(Student student) {
        logger.info("学生信息 班级:{} 人数:{}", student.clazz, student.count());
    }

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

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

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

相关文章

422规范详解

概述&#xff1a; 全称为EIA-TIA-422-B&#xff0c;于1994年发布。 典型电路由一个发送器和N个接收器以及一个中断匹配电阻组成。 发送器&#xff1a; 差分输出电压值在2V~10V之间。 4.1.1 发送器输出阻抗 要求A/B之间的差分阻抗≤100Ω。 4.1.2 开路特性 要求差分电压≤…

高阶MySQL语句

数据准备 create table ky30 (id int,name varchar(10) primary key not null ,score decimal(5,2),address varchar(20),hobbid int(5)); insert into ky30 values(1,liuyi,80,beijing,2); insert into ky30 values(2,wangwu,90,shengzheng,2); insert into ky30 values(3,lis…

手写Mybatis:第8章-把反射用到出神入化

文章目录 一、目标&#xff1a;元对象反射类二、设计&#xff1a;元对象反射类三、实现&#xff1a;元对象反射类3.1 工程结构3.2 元对象反射类关系图3.3 反射调用者3.3.1 统一调用者接口3.3.2 方法调用者3.3.3 getter 调用者3.3.4 setter 调用者 3.4 属性命名和分解标记3.4.1 …

Modbus TCP通信笔记

目录 1 Modbus TCP 数据协议1.1 数据格式1.2 报文头(MBAP头)1.3 功能码1.4 Modbus 地址映射到 CPU 地址 2 Modbus TCP 通讯数据示例2.1 功能码01 读离散输出线圈2.2 功能码02 读离散输入线圈2.3 功能码03 读保持寄存器2.4 功能码04 读输入寄存器2.5 功能码05 写单个离散输出寄存…

DevOps理念:开发与运维的融合

在现代软件开发领域&#xff0c;DevOps 不仅仅是一个流行的词汇&#xff0c;更是一种文化、一种哲学和一种方法论。DevOps 的核心理念是通过开发和运维之间的紧密合作&#xff0c;实现快速交付、高质量和持续创新。本文将深入探讨 DevOps 文化的重要性、原则以及如何在团队中实…

用Rust打印hello world!

步骤1 桌面新建1个名为 rustDemo 的文件夹&#xff08;文件夹名字随便取&#xff09; 步骤2 打开新建的文件夹&#xff0c;在地址输入栏输入 cmd 按回车键进入命令行窗口 步骤3 打开编译器&#xff0c;按 Ctrl S&#xff0c;保存文件到 rustDemo 文件夹中&#xff0c;保存的…

C++算法 —— 分治(1)快排

文章目录 1、颜色分类2、排序数组3、第k个最大的元素&#xff08;快速选择&#xff09;4、最小的k个数&#xff08;快速选择&#xff09; 分治&#xff0c;就是分而治之&#xff0c;把大问题划分成多个小问题&#xff0c;小问题再划分成更小的问题。像快排和归并排序就是分治思…

Linux网络编程 网络基础知识

目录 1.网络的历史和协议的分成 2.网络互联促成了TCP/IP协议的产生 3.网络的体系结构 4.TCP/IP协议族体系 5.网络各层的协议解释 6.网络的封包和拆包 7.网络预备知识 1.网络的历史和协议的分成 Internet-"冷战"的产物 1957年十月和十一月&#xff0c;前苏…

centOS下载与安装

1 下载centOS镜像 The CentOS Project 选择阿里云的镜像 2 下载虚拟机 Vmware workstation VMware - Delivering a Digital Foundation For Businesses 1 下载安装 centOs是一个操作系统&#xff0c;操作硬件的。所以需要有机器&#xff0c;可以使用虚拟机。 2 创建新的虚…

org.mockito:mockito-core 组件安全漏洞及健康度分析

组件简介 维护者mockito组织许可证类型MIT首次发布2008 年 4 月 29 日最新发布时间2023 年 8 月 27 日GitHub Star14159GitHub Fork2478依赖包24,748依赖存储库145,258 org.mockito:mockito-core是一个流行的 Java 模拟框架&#xff0c;它提供了一个简洁的 API 来创建和使用模拟…

【笔记】常用 js 函数

数组去重 Array.from(new Set()) 对象合并 Object.assign . 这里有个细节&#xff1a;当两个对象中含有key相同value不同时&#xff0c;会以 后面对象的key&#xff1a;value为准 保留小数点后几位 toFixed 注意&#xff1a; Number型&#xff0c;用该方法处理完&#xff0c;会…

Windows右键添加用 VSCODE 打开

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

O2OA(翱途)开发平台 V8.1正式发布

尊敬的O2OA(翱途)平台合作伙伴、用户以及亲爱的开发小伙伴们&#xff0c;平台 V8.1版本已正式发布。正值8月的最后一周&#xff0c;我们以更安全、更高效、更好用的崭新面貌迎接9月的到来。 O2OA开发平台v8.1版本更注重于对系统级别的安全防护。其中重大的更新&#xff0c;是对…

Re44:数据集 GSM8K 和 论文 Training Verifiers to Solve Math Word Problems

诸神缄默不语-个人CSDN博文目录 论文全名&#xff1a;Training Verifiers to Solve Math Word Problems GSM8K数据集原始论文 OpenAI 2021年的工作&#xff0c;关注解决MWP问题&#xff08;具体场景是小学&#xff08;grade school&#xff09;数学题&#xff09;&#xff0c…

Java设计模式:四、行为型模式-09:模板模式

文章目录 一、定义&#xff1a;模板模式二、模拟场景&#xff1a;模板模式三、改善代码&#xff1a;模板模式3.0 引入依赖3.1 工程结构3.2 模板模式结构图3.3 爬取商品生成海报实现3.3.1 HTTP获取连接类3.3.2 定义执行顺序的抽象类3.3.3 当当爬取抽象实现类3.3.4 京东爬取抽象实…

java八股文面试[多线程]——synchronized锁升级详细流程

偏向锁 偏向锁是JDK6中的重要引进&#xff0c;因为HotSpot作者经过研究实践发现&#xff0c;在大多数情况下&#xff0c;锁不仅不存在多线程竞争&#xff0c;而且总是由同一线程多次获得&#xff0c;为了让线程获得锁的代价更低&#xff0c;引进了偏向锁。 偏向锁是在单线程执…

python3.11教程1:python基础语法、程序控制、函数

文章目录 一、Python简介1.1 为什么学习python1.2 python安装与配置1.3 python解释器1.4 命令行参数1.4.1 sys.argv变量1.4.2 -c和-m选项 1.5 解释器的运行环境1.5.1 编码格式1.5.2 编码声明 二、Python基础语法2.1 行结构2.2 变量&#xff08;标识符&#xff09;2.3 字节串2.4…

如何让照片动起来?几步操作轻松动起来

现在&#xff0c;许多人都喜欢在社交媒体上分享自己的照片。但是&#xff0c;有时单张静态照片可能无法完全表达出你想要表达的感觉。为了使你的照片更生动有趣&#xff0c;你可以使用一些简单的技巧使它们动起来。下面是几个简单的步骤。 步骤1&#xff1a;打开制作应用并导入…

图像融合去雾、近红外去雾、(近)红外和可见光数据集

今天给大家分享一篇发表在IEEE TMM上的去雾文章Joint Contrast Enhancement and Exposure Fusion for Real-World Image Dehazing 作者从对比度增强和曝光融合的视角来解决图像去雾问题&#xff0c;在真实场景上取得了较好的去雾效果。此外&#xff0c;作者将所提出的方法应用…

Json解析流程

一、拿到了题库 分析一下可以定义的 1、序号&#xff0c;用来区分题目数&#xff0c;每个题有唯一的序号 2、题目&#xff0c;就是下图的Q 3、预设的回答&#xff0c;下图的A 分析完我可以知道有三个字段&#xff0c;分别是int index、string Q、string A。 二、把字段丢到…