面向对象第三单元总结

万家琛23371387 2025-05-17 19:55:47

单元架构

第九次作业

img

第十次作业

img

第十一次作业

img

本单元的测试过程分析

  1. 单元测试
    每个类(如 NetworkMessagePerson)的功能都通过独立的测试方法进行验证。
    例如,在 NetworkTest.java 中,针对 deleteColdEmoji(int limit) 方法设计了多个测试用例,分别验证删除热度低于阈值的 emoji、limit=0 的边界情况以及无 emoji 数据的情况。
    单元测试强调对单个函数或模块的隔离测试,确保每个组件在隔离环境下行为正确。

  2. 功能测试
    验证整个程序是否符合需求规格说明书(Spec)定义的行为。
    例如,测试 addRelation() 是否正确地建立关系、是否能正确抛出异常、是否更新连通块和三元环等。
    功能测试通常不关心内部实现,而是关注外部接口的输入输出是否符合预期。

  3. 集成测试
    测试不同模块之间的交互是否正确。例如,sendMessage() 可能涉及 MessagePersonEmoji 等多个类的协作。
    testDeleteColdEmoji_RemoveLowPopularityEmojis() 中,不仅测试了 emojiHeatList 的清理,还检查了与之相关的 messages 是否被正确删除。
    这种跨模块的数据一致性测试属于典型的集成测试范畴。

  4. 压力测试
    虽然未显式展示,但可以通过构造大量数据(如数万个 PersonMessage)来模拟高并发场景,测试系统的吞吐量和响应时间。
    压力测试可发现性能瓶颈,如某些算法的时间复杂度过高导致超时。

  5. 回归测试
    每次修改代码后重新运行所有测试用例,确保已有功能没有被破坏。
    JUnit 提供了良好的支持,只需执行全部 Test 类即可完成回归测试。


对各类测试的理解

测试类型定义特点
单元测试针对最小可测试单元(如函数)进行测试快速、自动化、细粒度
功能测试验证系统是否满足业务需求黑盒视角、端到端
集成测试验证多个模块组合后的交互逻辑是否正确关注接口、依赖
压力测试模拟高负载场景,测试系统极限性能强调并发、资源占用
回归测试修改代码后重新运行测试,防止引入新 bug自动化程度高、持续集成

数据构造策略

  1. 边界数据
    构造最小/最大值,如 id = 0 或 Integer.MAX_VALUE。
    如测试 deleteColdEmoji(0),验证当 limit 为 0 时不删除任何 emoji。

  2. 异常数据
    构造非法输入以触发异常,如重复的 ID、空对象、null 参数等。
    例如在 addMessage(MessageInterface message) 中,传入 null 或非法 EmojiId 来测试异常处理。

  3. 典型数据
    构造正常业务流程下的数据,如添加多个用户并建立关系。
    setUp() 中创建两个 Person 并建立关系,用于后续 sendMessage 测试。

  4. 随机数据
    使用工具生成大规模随机数据,用于压力测试或覆盖率提升。
    可使用 Java 的 Random 或第三方库(如 JUnitParams)辅助构造。

  5. 覆盖分支数据
    根据代码结构构造数据,确保每条 if-else、switch-case 分支都被执行。
    如在 sendMessage() 中,测试 type=0 和 type=1 两种情况,以及不同类型的 Message(如 RedEnvelope、Forward)。

  6. 历史 Bug 数据
    收集曾经修复过的 bug 所对应的输入数据,加入回归测试用例。
    保证同类问题不再复现。


总结

从单元到集成再到功能测试,层层递进,确保质量。
覆盖边界、异常、典型、分支等多类数据,提高测试覆盖率。
JUnit 等框架支持快速执行和回归测试。
采用多种策略构造数据,既能发现潜在缺陷,又能模拟真实场景。

利用大模型进行规格设计

根据案例中 MyJvm.createObject 方法 JML 规格的生成过程,总结引导大模型完成复杂任务的策略如下:


1. 分步骤需求拆解

场景:需要同时处理堆空间充足/不足时的对象创建逻辑
引导方式
先描述基础需求(创建对象+容量判断)
再补充触发 GC 后的对象保留规则
最后细化新旧对象关系的约束条件
示例

// 第一层:基础容量判断
requires \old(heap.size) < (DEFAULT_CAPACITY - count)
ensures heap.size == \old(heap.size) + count

// 第二层:GC触发后的容量约束
requires \old(heap.size) >= (DEFAULT_CAPACITY - count)
ensures heap.size == (\sum ... ) + count

// 第三层:新旧对象引用关系
(\forall int i; ...)

2. 强化不变量约束

场景:需要维护堆中对象的引用状态一致性
引导方式
明确要求保留所有 referenced 对象
强调新对象必须被完整添加
约束未被引用对象的清除规则
关键 JML 片段

ensures (\forall int i; 1 <= i && i <= \old(heap.size);
    \old(heap.getElement(i).isReferenced()) ==> 
        (\exists int j; ...)) // 保留被引用对象

3. 多场景覆盖

场景:需要区分正常创建与触发 GC 两种路径
引导方式
使用 also 子句分离不同行为
通过 requires 明确前置条件边界
ensures 描述不同路径的后置状态
实现形式

/*@ public normal_behavior
  @ requires 条件1...
  @ also
  @ requires 条件2... */

4. 提供领域知识

场景:需要准确表达 JVM 垃圾回收语义
引导方式
输入时明确 GC 的语义(如:只清除 unreferenced 对象)
提供 MyObject 类的关键属性(如 id 生成规则)
说明堆结构的特性(最小堆排序)


5. 反馈修正机制

场景:处理大模型生成的初始规格不完整的情况
典型修正过程

  1. 首先生成基础对象添加逻辑
  2. 补充 GC 触发后的容量计算公式 (\sum ...)
  3. 增加对象引用状态的全称量词约束
  4. 最终加入堆排序特性约束 compareTo

6. 结构化表达

优化点
使用 \forall\exists 量词明确对象关系
通过 \old() 准确标识初始状态
==> 运算符描述条件逻辑
优势

// 清晰表达引用对象保留规则
(\forall int i; ... 
    \old(...) ==> (\exists int j; ...))

通过以上策略,可系统性地引导大模型:

  1. 从功能描述 → 基础 JML 框架
  2. 从单场景 → 多路径覆盖
  3. 从笼统约束 → 精确定量描述
  4. 最终形成符合形式化验证要求的完整规格

本单元的架构设计与图模型构建维护策略

整体架构设计

  1. 核心类结构
    Network:主控类,负责管理所有对象(Person、Tag、Message、Emoji等),实现社交网络的核心功能。
    Person:表示用户节点,包含好友关系、标签、接收到的文章/消息等信息。
    Tag:表示标签,用于组织多个用户并提供统计信息(如年龄均值、方差、valueSum)。
    Message:消息抽象,支持点对点和广播两种类型,并派生出多种子类(RedEnvelope、Forward、Emoji)。
    MyUtil:工具类,封装 BFS、最佳熟人查询等算法。

  2. 接口规范

  3. 图模型构建方式
    图由 Person 节点构成,边为双向的 acquaintance 关系,用邻接表存储。
    Network 维护连通块集合 (blocks) 和三元环数量 (qtsum),以支持快速查询。
    每次添加/删除关系时都会动态更新图结构(如重新计算连通块、更新 bestAcquaintance)。

  4. 图模型维护策略
    连通性维护:使用 DFS 实现连通块划分,每次断边后重新生成连通块。
    三元环维护:在添加/删除边时遍历可能形成新三角形的第三节点,维护 qtsum
    bestAcquaintance 动态维护:当关系变化时,触发重计算,保证每个用户的“最亲密好友”始终正确。
    Tag 中 valueSum 的维护:通过 manageValueSum() 方法,在关系变更时同步更新 Tag 内部的 sum 值,避免重复计算。


性能问题及修复情况分析

常见性能问题

问题描述影响
O(N²) 遍历如在 queryTagValueSum()deleteBlock() 中频繁遍历所有用户或连通块时间复杂度过高,影响大规模测试
标签 valueSum 同步更新不及时多处修改关系未调用 manageValueSum(),导致查询结果错误数据一致性问题
BFS 查询效率低queryShortestPath() 中多次调用 BFS 导致超时查询响应时间长

优化手段

  1. 缓存机制
    bestAcquaintance, valueSum, qtsum 等高频查询字段进行动态维护,避免重复计算。
  2. 局部更新
    modifyRelation()addRelation() 等操作中,仅更新受影响的部分 Tag 和 bestAcquaintance。
  3. 提前剪枝
    在 BFS 中一旦找到目标路径立即返回,减少不必要的搜索。
  4. 使用高效容器
    HashMap<Integer, Person> 存储用户,O(1) 查找;
    ArrayList<HashMap<Integer, Person>> 存储连通块,便于增删;
    HashSet<Integer> 存储文章 ID,O(1) 判重。

对规格与实现分离的理解

  1. 规格定义清晰
    JML 规格明确约束了输入输出、异常抛出、不变量(invariant)、前置条件(requires)和后置条件(ensures)。
    示例:@requires containsTag(id); 明确要求调用前必须存在该 tag,否则抛异常。

  2. 实现灵活多样
    不同开发者可以采用不同数据结构(如链表 vs 数组)、算法(DFS vs 并查集)实现相同接口。
    只要满足 JML 规格,实现方式不影响外部调用者。

  3. 数据容器选择
    Tag 中使用 Person[] persons 存储成员,便于快速访问;
    Network 中使用 List<Tag> 存储标签,方便增删改查;
    Message 使用 Queue<Message> 缓冲待发送消息,提高并发处理能力。

  4. 动态维护机制
    Tag.valueSum 不在查询时计算,而是在每次关系变动时动态维护;
    这种策略牺牲部分写入性能换取读取效率,适用于读多写少场景。


如何利用规格信息设计 JUnit 测试

  1. 覆盖规格中的各种行为
    每个 @requires 条件都应有对应异常测试;
    每个 ensures 条件都应有结果验证;
    每个 invariant 都应在操作前后保持一致。

  2. 边界值测试
    测试空集合、单元素集合、最大值、最小值等边界情况;
    如测试 deleteColdEmoji(0)queryTripleSum() 在无三角形时返回 0。

  3. 状态变化测试
    测试方法执行前后系统状态是否符合预期;
    如添加关系后检查连通块是否合并、bestAcquaintance 是否更新。

  4. 异常测试
    使用 assertThrows() 检查是否抛出正确异常;
    如测试 addMessage() 传入非法 emojiId 是否抛出 EmojiIdNotFoundException

  5. 集成测试
    多个操作组合后是否仍满足规格;
    如先 addRelation(),再 modifyRelation(),最后 removeRelation(),最终状态是否回到初始。

  6. 一致性检验
    对比手动计算值与程序返回值是否一致;
    如构造一个简单图,手动计算 shortest path,然后调用 queryShortestPath() 验证。


JUnit 测试检验代码实现与规格的一致性效果

方面效果
异常检测高效发现未处理的边界条件,如未检查 id 是否合法
数据一致性有效识别 valueSumqtsum 等字段更新遗漏或错误
行为一致性确保不同实现方式下接口行为一致,如 equals()containsTag()
覆盖率提升通过构造典型、边界、异常数据,提高测试覆盖率
回归测试修改代码后自动运行测试套件,防止引入新 bug
文档辅助测试用例可作为 API 使用示例,帮助理解接口用途

本单元学习体会

  1. JML 与面向接口编程的重要性
    JML 提供了一种形式化的方式描述接口行为,极大提升了代码的可维护性和可扩展性;
    接口与实现分离,使得多人协作开发更高效,降低模块耦合度。

  2. 图建模与动态维护技巧
    图结构是本单元的核心,如何高效构建和维护图模型是关键;
    动态维护字段(如 bestAcquaintance、valueSum)需要合理设计触发时机,避免冗余计算。

  3. 性能优化意识增强
    面对大数据量时,简单的暴力算法容易超时;
    必须结合数据结构特性、缓存机制、局部更新等方式进行优化。

  4. 测试驱动开发理念深入
    先写测试再编码有助于理清需求边界;
    单元测试 + 集成测试双重保障,提升代码质量。

  5. 团队协作与规范统一
    在统一接口规范下,不同同学可独立开发不同模块;
    测试用例共享机制提高了整个系统的健壮性。


本单元通过 JML 接口规范引导我们完成了一个完整的社交网络模拟系统,涵盖了图建模、动态维护、性能优化等多个方面。借助 JUnit 测试框架,我们不仅验证了代码与规格的一致性,也提升了测试驱动开发的能力。整个过程加深了我们对接口设计、图算法、数据结构优化的理解,是一次非常有价值的学习体验。

...全文
14 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

271

社区成员

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

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