2026 BUAA_OO_Unit3总结

胡静伊-24373125 2026-05-29 00:16:38

对JML和规格驱动开发的理解

在接触规格驱动开发之前,我写代码往往是“边想边写”,想到哪里补哪里。但这次作业强制要求先阅读JML规格,这彻底改变了我的开发习惯。我对规格驱动开发的理解可以概括为两点:

  • JML就是一份严格的“契约”,分离“做什么”与“怎么做”。比如在 NetworkInterface 中,规格通过 requiresassignableensures 明确规定了方法的前置条件、副作用和后置条件。作为开发者,我只需要保证代码的执行结果满足 ensures 的约定即可,至于内部是使用哪种具体的数据结构,JML并不干涉。这给了我极大的底层实现自由度。

  • 自然语言的需求往往会遗漏边界情况,但JML的逻辑表达式却极其严谨。在实现 addUseruploadVideo 时,规格明确列出了抛出 EqualUserIdExceptionUserIdNotFoundException 等异常的精确条件。这逼迫我在编写核心逻辑前,必须先把防御性代码写好,大大减少了后期的Bug调试时间。


JUnit测试的经验总结

在这次作业中,我花了不少精力编写 NetWorkTest.java。通过编写测试,我总结出了几条行之有效的经验:

  • 测试社交网络最繁琐的就是造数据。为此,我专门写了 build1()build2() 这样的工厂方法,用来快速构建包含多个用户和复杂关注关系的初始网络。这让我的 @Test 方法变得非常干净,能够专注于测试具体的业务动作。

  • 我发现仅仅用 assertEquals 是不够的,对象的状态非常复杂。因此我自定义了 compareUsrcompareVid 方法。在验证用户是否相等时,我不仅仅对比引用,而是通过调用 strictEquals 方法严格校验用户的 ID、年龄和名字是否完全一致。

  • 我的测试用例没有死板地按方法分类,而是按照业务场景来覆盖,例如空图 (testEmpty)、单向/双向关注 (testSingleDir, testOnePair) 以及状态变更后的对比 (testAfterUnfollow)。


三次作业的迭代历程

  • 第一次作业网络还只是个简单的有向图。主要的挑战是图的广度优先搜索(BFS)求最短路径,以及维护互相关注的人数。在这个阶段,我学会了放弃 List 换成 HashMap,并初次尝试了动态维护(全局 sum)。

  • 第二次作业业务场景引入了视频分类、硬币、热度。JML 规格中开始出现大量的嵌套遍历(比如求年龄递减最长序列、求单个 UP 主的最佳贡献者)。纯粹的模拟已经行不通了。我在这时引入了分类索引videosByType)、脏数据标记(Dirty Bit)记忆化搜索memo),让查询从 O(N) 降维打击到 O(1)。

  • 第三次作业全局最佳贡献者、个性化视频推荐(recommendVideo)、UP主推荐(recommendNthUp)等极度消耗性能的需求接踵而至。JML 规格写得轻描淡写,但如果照抄规格,复杂度可以到 $O(N^3)$。在这一代代码中,我再次利用缓存缓存机制,做到了循环内的常数级优化。


如何发现已有方法/容器在迭代中的变化?

  • 当 JML 的 ensures 子句中出现了对特定属性的筛选条件时,就是重构容器的信号。比如HW2 中新增了按 type 查询最热视频的规格。如果只用全局的 HashMap<Integer, Video>,每次都要遍历全网视频判断类型。因此我立刻写出了 HashMap<String, List<Video>> videosByType 这个二维容器,这就是“基于查询维度的索引化”。

如何精准揪出并消灭程序的性能瓶颈?

虽然JML只描述逻辑,但我们可以通过“数理化”它的规格来预判性能瓶颈。总结下来,揪出瓶颈的方法有以下三招:

  • 如果在 JML 中看到一个 \max 里面套着一个 \sum,这在代码里大概率就是一个 O(n^2) 的双重循环,绝对会 TLE。比如说在 HW3 中求全局最佳贡献者(queryGlobalBestContributor),如果现场算,需要遍历所有 UP 主,再遍历他们的粉丝。为了解决这个瓶颈,我在 Network 中引入了 bestCounts 容器。在每一次发生 coinVideo(投币)动作时,顺手计算并更新旧的 bestContributor 和新的 bestContributor 的次数。将原本 O(n^2) 的查询,平摊到了每一次投币的 O(1) 操作中。

  • 识别“读多写少”的图查询,引入“脏标记缓存”,当一个涉及全图遍历的查询(如 HW2 的求最长年龄递减序列 queryLongestDecSeq)可能被连续调用多次,而中间没有任何节点发生变化时,重复搜索是极大的浪费。我设计了 dec 缓存和 memo。只有当 addUserfollowUser 等真正改变图拓扑的写操作发生时,才调用 graphChange() 清空缓存。这种机制让连续查询瞬间返回。

分析自己程序出现过的bug以及Bug出现的原因

本单元作业只出现了两个bug,一个是第一次作业的时候没有注意控制时间复杂度,导致双层循环超时,后期一直注意减少时间复杂度,没有再TLE过。
第二个bug是因为删除评论包含的字符串参数可能为空,并没有注意到这个细节,所以出错了。

Unit3第二次研讨课上,JML“击鼓传花”游戏的感悟: 你是否发现了自己/别人的JML的bug? 在传递过程中,需求,边界是否发生了变化? 今后多人组队编程时,你认为怎么做才能统一所有人对任务需求和实现方法的理解?采用什么措施(或者指定规则)可以减少组内成员间的信息差?

我发现有人在传递过程中漏写了相关变量的类型,比如整型,而我自己也不小心看错了一次数据的边界条件,导致数据范围变化。
我认为还是JML和自然语言双重验证比较合适,并且善用git,让所有人对彼此的改变可见,也可以维护一个共享文档,撰写要求的人可以加粗标出自己认为别人可能遗漏的地方,并且支持大家随时询问和回答。

...全文
22 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
代码下载链接: https://pan.quark.cn/s/cf0000dae7ac 在.NET Framework平台中,`TreeView`组件是一种普遍应用的数据展示工具,主要功能是呈现层级化数据,例如文件系统布局、组织架构图等。本文将深入阐述在C#环境下如何运用递归方法为`TreeView`组件配置子节点,尤其是在管理文件夹层次结构的应用场景中。递归是一种高效的编程策略,其特点在于函数能够自我调用以完成特定任务,这种技术特别适用于处理具有层级关联的数据集合。为了有效运用`TreeView`组件,我们首先需要明确其核心构成单元:`TreeNode`。`TreeNode`是`TreeView`中的一个基本单元,它可以承载子节点,从而构建出树状结构。为了在`TreeView`中准确反映文件夹结构,每一个`TreeNode`通常映射为一个文件夹,而其下属的子节点则对应该文件夹内的子文件夹或文件。现在我们聚焦于核心内容,探讨如何通过递归方式实现子节点的添加。1. **构建基础框架** 我们需要设计一个类来描述文件或文件夹,该类应包含名称、路径等基本属性。例如: ```csharp public class FileSystemItem { public string Name { get; set; } public string Path { get; set; } // 其他属性如IsDirectory等 } ```2. **采集文件系统数据** 借助`System.IO`命名空间中的`DirectoryInfo`和`FileInfo`类,对目标目录进行遍历,以获取所有文件和子文件夹的信息。这里可以利用`GetDirectories()`和`GetFiles...
内容概要:本文系统阐述了Java微服务架构与TypeScript全栈工程化的实战方法,涵盖从单体应用拆分到分布式系统治理的完整技术链条。在Java微服务部分,基于Spring Boot与Spring Cloud生态,深入讲解领域驱动设计(DDD)、服务注册与发现(如Nacos、Eureka)、配置中心、API网关(Spring Cloud Gateway)、声明式调用(Feign)、负载均衡、服务熔断降级(Resilience4j/Hystrix)、消息队列异步解耦(Kafka/RabbitMQ)以及分布式事务(如Seata)等核心技术。数据层强调数据库自治原则,并结合Redis提升性能。前端部分聚焦TypeScript类型系统,通过静态类型检查增强代码可靠性,支持泛型、联合类型、映射类型等高级特性,实现前后端接口模型统一。全栈协作采用React/Vue/Angular框架,结合Axios通信与Swagger接口文档标准化。工程化层面引入Docker、Kubernetes实现容器化部署,配合Jenkins或GitHub Actions完成CI/CD自动化流程,并通过ELK实现日志追踪。典型应用场景包括电商、订单管理等系统,实现高内聚、低耦合、可扩展的分布式架构。; 适合人群:具备一定Java与前端基础,从事中高级后端开发、全栈开发或系统架构工作的技术人员,尤其适合1-5年经验并希望掌握微服务与全栈工程化实践的研发人员。; 使用场景及目标:①掌握微服务拆分与Spring Cloud微服务体系建设;②理解服务治理、异步通信、分布式事务等关键问题的解决方案;③构建类型安全的全栈项目,提升前后端协作效率与系统稳定性;④实现微服务的容器化部署与持续交付。; 阅读建议:建议结合实际项目边学边练,重点关注架构设计思想与技术选型背后的权衡,同时动手搭建完整微服务链路与前端类型系统,深入理解各组件集成方式与最佳实践。

307

社区成员

发帖
与我相关
我的任务
社区描述
2026年北航面向对象设计与构造
java 高校
社区管理员
  • 孙琦航
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧