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

简化Java单元测试数据

用EasyModeling简化Java单元测试

EasyModeling 是我在2021年圣诞假期期间开发的一个 Java 注解处理器,采用 Apache-2.0 开源协议。它可以帮助 Java 单元测试的编写者快速构造用于测试的数据模型实例,简化 Java 项目在单元测试中准备测试数据的工作,在提高编写效率的同时,使单元测试更加整洁易读。经过一年的维护,EasyModeling 已经在几个 Thoughtworks 内部的项目上得到了应用,并迭代发布了几个版本。

单元测试中的数据准备的困难

在企业级应用软件开发项目中编写测试代码时,针对特定的测试场景,我们需要准备相应的测试数据,以验证被测组件在给定输入下的行为。在使用 Java 语言的项目中,这些准备测试数据的代码体现为创建各种“数据模型类”的实例。这里的数据模型类,可以包括聚合模型(Aggregation Model)、数据传递模型(DTO)、值对象(VO)以及存储模型(Persist Model)等等。无论是对服务组件的测试,还是对数据模型本身的测试,我们都无可避免地需要构建这些数据模型类的实例。

在项目的起初阶段,准备数据的工作是简单的,我们只需要调用数据模型类的构造方法,传入适当的参数来创建实例即可。单元测试代码的规模不会太大,也尚且清晰易读。

但是随着产品开发工作的展开,一方面,项目中使用的这些数据模型会变得越来越复杂;另一方面,测试场景也会变得越来越多。经验上,在经过几个版本迭代的企业级应用 Java 代码中,我们通常不难找出一些拥有十几个、甚至几十个成员变量的数据模型类,并且它们之间还存在着复杂的相互持有、嵌套、继承的关系。这些数据模型类往往都是项目中的核心组件,故而也成为单元测试需要重点关注的组件。相应地,在涉及这些数据模型的单元测试中,为准备测试数据而编写的初始化数据模型类的代码量也会越来越大、越来越复杂。

这些冗杂繁复的数据初始化代码会影响单元测试本身的代码质量,造成单元测试编写成本高、易读性差、易维护性低等问题。而单元测试的质量又与生产代码的质量息息相关。例如,单元测试的编写成本过高,会使开发者越来越倾向于仅在已有测试基础上做修改,而不是为每个场景创建单独的测试,造成单个测试的职责过多;甚至使开发者放弃单元测试,降低了团队对产品质量的信心。又比如,单元测试的易读性差,导致单元测试无法承担起“测试即文档(tests as documentation)”的职责。而单元测试的易维护性低,则导致了代码很难被重构,从而单元测试不仅没有为重构提供信心,反而变成重构的桎梏。

具体来说,这些初始化数据的代码会引起三个方面的问题:

  • 对测试场景的描述不清晰
  • 构建测试数据的代码重复
  • 初始化数据模型代码的膨胀

我们可以从下面的例子中略窥端倪。你是否在你的项目中见过这样的单元测试?

这是一段典型的使用JUnit测试框架的单元测试代码。在这段单元测试代码中,被测对象是 leaveCalculator 组件的 annualLeave 方法。我们首先创建一位员工,如(a)处;然后将创建好的员工对象传入 annualLeave 方法,为其计算出应得的年假数额,如(2)处;最后断言他应该享有20天年假,如(3)处。为了简化讨论,我们暂且假设此处 annualLeave 方法的业务规则是:员工应得的年假数额只与这位员工加入公司的时间(date of joining)相关,即在代码中 (1) 处初始化的日期。

我们来详细分析这段测试代码中存在的坏味道、以及其潜在的问题。

对测试场景的描述不清晰

如前文所述,我们假设这段单元测试代码的目的是验证“入职超过5年的员工应该享有20天年假”这个业务规则。那么显然,其中只有 (1), (2), (3) 这三处是与当前测试场景相关的,它们共同构成了对上述业务规则的描述。而在 (1) 处之前传入 Employee 类构造方法的那些参数都是与当前测试场景无关的。遗憾的是,这些与测试场景无关的代码却占据了这个代码片段中的绝大部分代码行。

在实际项目中,我们会见到很多这样的单元测试,它们往往需要用几十行的代码来准备复杂的测试数据,需要初始化数个数据模型类的对象,以支持对被测组件的调用,然而这些代码中真正在描述测试场景的,却只有其中区区几行、甚至一两行。这不仅增加了测试的篇幅,还会导致阅读者无法快速聚焦在有意义的初始化条件上。就像我们在这个例子中看到的,描述测试场景的代码行(1)处混杂在大量初始化测试数据的代码行之中,造成了单元测试对测试场景的描述不聚焦。这使单元测试的阅读者很难从这段测试代码中一目了然地理解测试的意图,更遑论以测试为文档来理解业务规则。而在测试失败时,也无法快速从测试场景的数据构造出发去定位问题。

一些有经验的单元测试编写者已经注意到了这个问题,他们会在关键的测试数据初始化行末添加一些注释以示强调。然而注释本身就预示着代码坏味道,并且在重构中也是非常不安全的,甚至反而误导读者。

构建测试数据的代码重复

如果将目光从单个测试放大到单元测试组(Test Suit),我们会发现在针对同一个被测组件的不同测试场景下,初始化数据模型的代码会大量重复。例如在针对员工年假数额计算(leaveCalculator 组件的 annualLeave 方法)的测试组中,假设按照业务规则,我们需要考虑以下的测试场景:

  1. 入职不足2年的员工,应该享有10天年假;
  2. 当年入职的员工,享有按照入职时间折算的年假数额;
  3. 入职超过2年,而不足5年的员工,应该享有15天年假;
  4. 入职超过5年的员工,应该享有20天年假;
  5. 入职超过7年的员工,应该享有25天年假;
  6. 入职时间在未来(尚未入职)的员工,不应该计算年假数额(抛出异常);

不难想象,我们会分别在这6个测试场景对应的测试方法中重复地编写几乎完全相同的代码来初始化Employee类的对象。

这样的单元测试模式在企业级应用开发的场景中比比皆是。开发者经常很容易在测试第二个场景时,顺手从第一个场景的单元测试中复制初始化数据模型的代码,略作修改来描述第二个测试场景,后面的测试场景也如法炮制。这样显然会造成测试代码中存在大量的模板代码(Boilerplate code),进一步降低了代码的易读性。

通常在开发项目的实践中会引入构建者模式(Builder Pattern)或者 Object Mother 组件来消除这些模板代码。本文非常欣赏这些解决方案,下文会在此基础上做进一步讨论。

初始化数据模型代码膨胀

另外需要注意的是,前文举例的代码中为节省篇幅已经做了很多简化。我们不仅用省略号折叠了(1)处之后可能传入构造方法的更多的初始化参数,还折叠了在(b)处初始化 List<Department> departments 参数时逐个构造 Department 类对象所需要的大量细节,甚至在初始化每个Department类对象时,又另外需要构造更多的相关实例。

当然在实践中,经常使用的策略是将大量无关的属性设置成 null 或者空集合,但是这有时候会在被测组件对数据类有效性检查中被拦截。特别是在某些演进了一段时间的代码库中,我们经常会遇到的困难是,由于在测试中构造数据时采用了过多的 null 和空集合,一个新添加的数据有效性检查步骤或者切面(AOP),会造成几百个单元测试的失败。逐一修复这些失败的单元测试的工作量无疑是巨大的,同时是充满风险的,因为此时对单元测试的修改完全是为了兼容一个新添加的切面,而脱离了单元测试本身的业务上下文。

在这种情况下,开发者会越来越多选择将相似的数据有效性检查步骤散布在具体的业务代码中,而非在构造方法中统一检查、或者通过切面集中实现。可见,单元测试的不良设计,会反过来增加生产代码的维护难度,拖累了生产代码的演进。

EasyModeling提供的能力

造成开发者写出类似单元测试的原因是广泛存在的。例如,Employee 类没有提供更灵活的构造方法,也没有 Builder 模式的构造器。从 Employee 类自身的职责的角度出发,它的确没有理由提供一个仅包含 LocalDate dateOfJoining 作为参数的构造方法。在很多业务场景下,数据模型类也完全有可能就是不允许通过 Builder 模式来构造的。我们当然不能为了编写测试代码的便利,而去修改生产实现代码。又例如,代码中可能存在对 Employee 类的数据合法性校验。这些校验可能是类似切面的形式存在的,导致我们无法方便地在单元测试中忽略它。

在实际项目中,开发者很容易从“消除重复”的角度,抽象出相应的工厂类来提供测试所需要的数据模型实例。Martin Fowler 也在他的博客的短文 Object Mother 中简要讨论了相关的思路。但是在测试中使用工厂组件虽然消除了很多重复代码,却没有提供针对不同的测试场景的灵活定制能力,因此一些项目又会同时采用 Builder 模式来提供定制能力。我自己在多个项目上引入 Object Mother 来提供测试数据实例后发现,这些工厂类本身又具有非常固定的代码模板,于是我开始考虑开发一个工具来自动生成这种工厂类。

受到 Builder 模式和 Object Mother 思想的启发,我开发了 EasyModeling 来尝试简化 Java 单元测试的编写,并提高测试的可读性和易维护性。EasyModeling 是一个 Java 注解处理器库,它主要提供三个方面的功能:

  1. EasyModeling在编译期根据指定的数据模型类的结构,生成对应的数据模型工厂类,以方便单元测试快速生成数据模型类的实例。通过向 EasyModeling 注册一个数据模型类,单元测试的编写者只需要调用 EasyModeling 所提供工厂类的静态方法,就可以立即得到这个数据模型类的实例。
  2. EasyModeling 还可以在单元测试的运行时,自动初始化它所生成的数据模型实例。在生成数据模型实例时,EasyModeling 默认的行为是给数据模型实例的字段填充随机值,让开发者不需要再耗费精力去填充对测试场景无意义的属性。同时,开发者仍然有机会向 EasyModeling 指定每个数据模型类的每个字段所需的初始化方式。
  3. 另外,EasyModeling 还在其生成的工厂类中提供了一个 Builder 模式的构建器。利用这个构建器,开发者可以定制、并仅定制与当前测试场景相关的字段,使单元测试简短、清晰、易读。

在编码层面,EasyModeling 的行为完全发生在测试包中,丝毫不会侵入项目的生产实现代码。同时,EasyModeling 只会照顾开发者向它注册的数据类型类,而不会在代码库中主动搜索。所以即使是维护已久的代码库,从任何时间点引入 EasyModeling 都不会造成额外的负担。

EasyModeling简化后的单元测试

在引入了 EasyModeling 后,本文中第一节中的单元测试例子可以得到显著地简化:

除此之外,如前文提到,开发者需要在测试代码中向 EasyModeling 注册 Employee 类:

首先我们看到,在引入 EasyModeling 后,单元测试的代码在篇幅上得到了非常明显地简化。在单元测试中 (4) 处,EmployeeModeler 类就是由 EasyModeling 在编译期生成的工厂类,通过引用 EmployeeModeler 类中的静态方法 builder(),我们可以得到 Employee 类的Builder 的实例。请注意,此处使用的 Builder 类不是由 Employee 类自己编写的,也不是通过如 Lombok 这样的工具来提供的,而是由 EasyModeling 在其生成的工厂类 EmployeeModeler 来提供的。这样的好处是,为了测试而准备的 Builder 完全没有侵入生产代码。

其次,在 (4) 处生成的 Builder 类的实例中,EasyModeling 已经为我们尽可能多地填充了所有的成员变量。因此,我们接下来只需要聚焦在当前测试场景所关心的成员变量上。例如在 (5) 处,我们将 dateOfJoining 字段的内容设置为指定的日期。在可读性方面,由于避免了冗长的初始化参数,所以使开发者在阅读单元测试时,能够快速理解测试场景,进而也比较容易修改或维护单元测试。

第三,EasyModeling 在填充数据模型实例的属性时,不仅能够填充一些 Java 应用中常用的数据类型,包括基本类型、数组、集合、时间日期等等,还能够进一步填充当前数据模型所引用的其他数据模型。例如 Employee 类中引用的 List<Department> departments 列表字段。

最后,为了让 EasyModeling 帮我们生成 Employee 类的工厂类,如以上代码中 (6) 处,开发者只需要在任意的一个类上通过 @Model 注解声明即可。EasyModeling在编译期为所有被 @Model 注解声明的数据模型类生成对应的工厂(Modeler)类。

除此之外,EasyModeling 还提供了其他一些好用的特性,限于篇幅,具体的用法请参考文档。

EasyModeling的不足和未来

但是由于我的业余精力和能力都非常有限,EasyModeling 目前还处于它成长的初期,存在几点显然的不足。

第一,没有维护良好的使用文档。目前我只维护了一份项目 Readme 文件,作为简要的使用文档,导致一些略高级的使用方法和一些从新版本开始支持的功能并没有体现在文档中。

第二,没有维护文档注释。遵循代码整洁的原则,在长期从事的企业应用开发中,我几乎不会写任何形式的注释。所以我也没有意识到,在维护一个更偏底层的开源工具库时,充分的文档注释是非常必要的。一方面,文档注释便于开发者用户查看阅读,也便于有兴趣的贡献者参与开发。另一方面,由于这种较为基层的工具中无可避免地要使用一些魔法,如果没有良好的注释,随着时间推移,可能连我自己也会忘记其中的细节。

由于 EasyModeling 是一个关注单元测试的工具,而不会入侵任何生产代码,因此,在 Java 项目中引入 EasyModeling 几乎不会对项目的可靠性、安全性造成任何风险。所以如果你对这个工具感兴趣,认为它有可能帮助你提高编写测试的效率,请不妨引入到你的项目中尝试使用。

未来,由于我自己在项目上会持续使用 EasyModeling 来构建测试数据,所以我基本可以保证持续维护这个工具。在近期,我将聚焦在完善使用文档,以及修复从用户反馈的一些缺陷。在EasyModeling 的功能特性方面,虽然我手上目前依然积压着一些我自己想要实现的功能,但是我更想从用户的反馈中收集更多有趣的好主意,再来推进下一阶段的功能演进。


文/Tthoughtworks张哲
原文链接:用EasyModeling简化Java单元测试-Thoughtworks洞见

相关文章:

简化Java单元测试数据

用EasyModeling简化Java单元测试 EasyModeling 是我在2021年圣诞假期期间开发的一个 Java 注解处理器&#xff0c;采用 Apache-2.0 开源协议。它可以帮助 Java 单元测试的编写者快速构造用于测试的数据模型实例&#xff0c;简化 Java 项目在单元测试中准备测试数据的工作&…...

P1041 [NOIP2003 提高组] 传染病控制

题目 题目背景 本题是错题&#xff0c;后来被证明没有靠谱的多项式复杂度的做法。测试数据非常的水&#xff0c;各种玄学-做法都可以通过&#xff0c;不代表算法正确。因此本题题目和数据仅供参考。 近来&#xff0c;一种新的传染病肆虐全球。蓬莱国也发现了零星感染者&#…...

TypeScript -- 基础类型

文章目录 TypeScript -- 基础类型let 和 const基本类型写法布尔类型 -- boolean数字类型 -- number字符串类型 -- string数组类型元组类型枚举类型 -- enum任意类型 -- any空值 -- voidNull 和 Undefined不存在的类型 -- never对象 -- object类型断言 TypeScript – 基础类型 1…...

Cookie 与 Session 的作用及区别、结合使用

Cookie的作用 在网站中&#xff0c;http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后&#xff0c;第二次请求服务器依然不能知道当前请求是哪个用户。 Cookie的出现就是为了解决这个问题&#xff0c;第一次登录后服务器返回一些数据&#xff08;Cookie&a…...

【Redis】面试题

1. 为什么要用缓存 1. 提高系统的读写性能。 2. 减轻数据库的压力&#xff0c;防止大量的请求到达数据库&#xff0c;让数据库压力剧增&#xff0c;拖垮数据库。redis数据存储在内存中&#xff0c;高效的数据结构&#xff0c;读写数据比数据库快。 将热点数据存储在redis当中&…...

(学习笔记-硬件结构)CPU如何执行程序?

冯诺依曼模型 冯诺依曼模型主要由五部分组成&#xff1a;运算器、控制器、存储器、输入设备、输出设备。 控制器&#xff08;Control Unit&#xff09;&#xff1a;从内存中取指令、翻译指令、分析指令&#xff0c;然后根据指令的内存向有关部件发送控制命令&#xff0c;控制相…...

curl: (26) Failed to open/read local data from file/application

Windows10、Windows环境用curl命令上传文件报错&#xff1a; curl: (26) Failed to open/read local data from file/application假设我要上传的文件目录是&#xff1a; F:\我的下载\test.xlsx 错误写法1&#xff1a;使用单引号 curl -X POST "https://xxx/upload&quo…...

2023年深圳杯数学建模 D题 基于机理的致伤工具推断

致伤工具的推断一直是法医工作中的热点和难点。由于作用位置、作用方式的不同&#xff0c;相同的致伤工具在人体组织上会形成不同的损伤形态&#xff0c;不同的致伤工具也可能形成相同的损伤形态。致伤工具品种繁多、形态各异&#xff0c;但大致可分为两类&#xff1a;锐器&…...

DMA传输原理与实现详解(超详细)

DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;是一种计算机数据传输方式&#xff0c;允许外围设备直接访问系统内存&#xff0c;而无需CPU的干预。 文章目录 Part 1: DMA的工作原理配置阶段&#xff1a;数据传输阶段&#xff1a; Part 2: DMA数据…...

【《React Hooks实战》——指导你使用hook开发性能优秀可复用性高的React组件】

使用React Hooks后&#xff0c;你很快就会发现&#xff0c;代码变得更具有组织性且更易于维护。React Hooks是旨在为用户提供跨组件的重用功能和共享功能的JavaScript函数。利用React Hooks&#xff0c; 可以将组件分成多个函数、管理状态和副作用&#xff0c;并且不必声明类即…...

Ajax详细讲解

Ajax&#xff08;Asynchronous JavaScript And XML&#xff09;即异步 JavaScript 和 XML&#xff0c;是一组用于在网页上进行异步数据交换的 Web 开发技术&#xff0c;可以在不刷新整个页面的情况下向服务器发起请求并获取数据&#xff0c;然后将数据插入到网页中的某个位置。…...

黑苹果如何在macOS Sonoma中驱动博通网卡

准备资源&#xff08;百度&#xff1a;黑果魏叔 下载&#xff09; 资源包中包含&#xff1a;AirportBrcmFixup.kext/IOSkywalkFamily.kext/IO80211FamilyLegacy.kext/OpenCore-Patcher 使用方法&#xff1a; 1.将 csr-active-config 设置为 03080000 全选代码 复制 2.在 …...

JVM-Cpu飙升排查及解决

https://blog.csdn.net/m0_37542440/article/details/123679011 1. 问题情况 在服务器上执行某个任务时&#xff0c;系统突然运行缓慢&#xff0c;top 发现cpu飙升&#xff0c;一度接近100%&#xff0c;最终导致服务假死。 2. 问题排查 1. 执行 “top” 命令&#xff1a;查看所…...

exoplayer3 ffmpeg 扩展库编译 aar,导入集成

exoplayer3 ffmpeg 扩展库编译 aar&#xff0c;导入集成。 已经编译完成的aar&#xff1a;https://download.csdn.net/download/mhhyoucom/88086822 编译项目方法&#xff1a; github下载项目&#xff1a;https://github.com/google/ExoPlayer FFmpeg 模块提供 &#xff0c;…...

Shell免交互

免交互 免交互就是&#xff1a;不需要人为控制就可以完成的自动化操作&#xff0c;自动化运维 Shell脚本和免交互是一个概念&#xff0c;是有两种写法。 Here Document 免交互 使用I/O(输入/输出)重定向的方式将命令的列表提供给交互式的程序或者命令cat read 是一种标准输入…...

设计模式之四:工厂模式

引言&#xff1a;除了使用new操作符之外&#xff0c;还有更多制造对象的方法。同时&#xff0c;实例化这个活动不应该总是公开地进行。 1.简单工厂模式 这里有一些相关的具体类&#xff0c;要在运行时有一些具体条件来决定究竟实例化哪个类。这样的代码&#xff08;if..elseif…...

斩获CVPR 2023竞赛2项冠军|美团街景理解中视觉分割技术的探索与应用

总第569篇 2023年 第021篇 视觉分割技术在街景理解中具有重要地位&#xff0c;同时也面临诸多挑战。美团街景理解团队经过长期探索&#xff0c;构建了一套兼顾精度与效率的分割技术体系&#xff0c;在应用中取得了显著效果。同时&#xff0c;相关技术斩获了CVPR 2023竞赛2项冠军…...

UE4/5C++多线程插件制作(十五、将模板统一,修改统一后的其他类,修改继承,修改返回类型等)

目录 MTPManageBase.h MTPAbandonable.h MTPAbandonableManage.h MTPThreadInterface.h MTPThreadAgendyManage.h MTPThreadTaskManage.h MTPManage.cpp MTPThreadTaskManage.h...

K8S系统监控:使用Metrics Server和Prometheus

Kubernetes 也提供了类似的linux top的命令&#xff0c;就是 kubectl top&#xff0c;不过默认情况下这个命令不会生效&#xff0c;必须要安装一个插件 Metrics Server 才可以。 Metrics Server 是一个专门用来收集 Kubernetes 核心资源指标&#xff08;metrics&#xff09;的…...

数据结构基础之排序算法

在数据结构中&#xff0c;常见的排序算法有以下几种&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;通过比较相邻元素并交换它们的位置&#xff0c;每轮将最大&#xff08;或最小&#xff09;的元素冒泡到末尾&#xff0c;重复执行直到排序完成。 fun…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

高考志愿填报管理系统---开发介绍

高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发&#xff0c;采用现代化的Web技术&#xff0c;为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## &#x1f4cb; 系统概述 ### &#x1f3af; 系统定…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...