OO 第三单元博客作业

祁明礼-21376220 学生 2024-05-19 14:18:33

目录

  • 测试过程
  • 黑箱测试 白箱测试
  • 单元测试 功能测试 集成测试 压力测试 回归测试
  • 数据构造策略
  • 作业设计
  • 架构
  • 性能
  • 规格与实现
  • 规格与设计分离
  • 第一次hw
  • delete
  • 第二次hw
  • 高中数学的基础
  • 动态维护
  • 最短路径
  • 第三次hw
  • bug分析
  • Junit测试
  • 学习体会

测试过程

黑箱测试 白箱测试

  • 黑箱测试: 从使用者的角度出发进行测试 检验程序能否满足需要指定的接口功能 黑箱测试不需要了解软件内部结构、实现细节和代码。黑箱测试关注的是输入数据和输出结果是否符合预期。测试无需编程知识,可以从用户的角度来进行,覆盖面广。但可能无法覆盖到所有的代码路径,无法发现代码内部的某些缺陷
  • 白箱测试: 在已知内部结构的前提下进行测试,检查程序内部实现是否满足要求,如在知道某个方法的内部实现是三重遍历后检查其是否满足时间需求。白箱测试需要了解软件的内部结构和代码实现,通过测试代码的各个路径、分支和逻辑流来验证软件的正确性。白箱测试可以覆盖到所有的代码路径,有助于发现代码中的隐藏错误和边界条件错误。但需要对代码有深入了解,编写和维护测试用例的成本较高。

单元测试 功能测试 集成测试 压力测试 回归测试

  • 单元测试: 是对软件系统的最小可测试部件进行测试,通常是对函数、方法或类进行独立验证。单元测试可以确保每个单元的功能正确性,并能快速定位和修复错误,保证代码的稳定性和质量
  • 功能测试: 根据软件需求说明书,验证软件系统每个功能是否都能按预期正确工作。功能测试可以验证软件的功能是否符合预期需求,一般采用黑箱测试方法
  • 集成测试: 将各个单独的模块按照设计要求组装成一个系统或子系统后进行测试,检测模块之间的接口和交互。集成测试可以发现模块之间的接口错误和集成问题,可以采用白箱测试或黑箱测试方法
  • 压力测试: 通过超负荷的测试条件来验证系统的稳定性和性能,例如高并发、大数据量等 压力测试可以评估系统在极端条件下的表现,找出系统的瓶颈和最大承受能力,通常采用模拟大量用户请求、数据处理等场景
  • 回归测试: 是对软件进行修改后,重新进行测试,验证修改未引入新的错误。回归测试可以确保新代码没有破坏现有功能,软件质量得到保持,可以通过自动化测试工具实现

数据构造策略

本单元的数据构造并不是很容易
随机生成的数据可能在图的结构中不能覆盖很多的可能性

我选择了使用极端数据进行压力测试
比如id数据类型是int 那么就可以使用int类型的最大值和最小值进行测试 看代码能够正常工作
在第二次作业使用极端数据成功hack了一次

包括我自己在第三次作业被hack的数据也是某种程度的极端数据(下文提到)

作业设计

架构

这里自己发挥的不多 基本沿用JML的规定
采用邻接表来构建图 数组大多采用HashMap 保证查询的高效
因为测试数据整体来说对查询的压力高于构建

从数据结构上看
Network类为主体 其中图的节点类是Person 构建了带权(value)无向图 显然的邻接表式(熟人)

Tag 依赖于Person Messages则是在Network和Tag的基础上的结构

性能

规格与实现

规格与设计分离

规格与设计分离的原则是指在软件开发过程中,我们可以分别独立地描述软件的“什么”和“如何”。即,我们将软件的功能需求或业务逻辑(即规格)与软件的实现细节或内部结构(即设计)显式地分离开。

这样做的好处

第一次hw

上面也提到了 规格只是一种对于接口的约束 而不是建议的实现方式
我在没有领会到这一点的情况下 按照JML写了方法 包括但不限于在 三元环那里使用了三重循环
在ddl当天 学习了并查集 将实现方式进行了修改

第一次需要注意的是

  1. 动态维护value
  2. 使用并查集维护block
  3. 在维护block的同时维护三元环数目

这样做基础是构建压力小于查询 但实际上构建少于查询是合理的
而且这样在时间复杂度上也更为合理 避免了O(n2) O(n3)

delete

需要注意的是 使用并查集 在增加关系 和 查询上都是 常数级
但是涉及到删除 就是O(n) 相对来说麻烦一点

img

思路上 在删边时 判断删除后id1点的block有没有id2点 如果有 说明删了一条边还有边使之联通
就不用管了
如果不在 说明原来的block 就要分成两个
分成两个的操作是常数级

只是建立1的block 需要dfs 这个时间复杂度也是可以接受的

第二次hw

高中数学的基础

在涉及agevalue 和 方差的查询上 可以动态维护 Sum 和 SumSqure
也就是和 以及 平方和

img

然后通过恒等变形就可以得到动态维护下的常数级查询
注意一点 getMean 是截断过的值就行了

动态维护

值得注意的是 Tag的增加 导致在 modifyRelation 和 addRelation中 增加了维护的需求

然后是couple 使用了脏位coupleDirty
我在修改脏位上的逻辑没有细化 比如addRelation时就直接coupleDirty=true
modifyRelation时稍微细化了一下 判断getBestAcquaintance的前后变化情况

然后在脏位修改的情况下的查询的时间复杂度也就是O(n)

最短路径

我分别使用了bfs和迪杰斯特拉 感觉差别不是很大 因为迪杰斯特拉用了优先队列
正常情况下还是用bfs

第三次hw

在性能上的要求不是很明显
按JML的规定来编写 自由发挥的空间不是很大

bug分析

前两次作业未出现问题
第三次作业出现逻辑上的漏洞

img

第一行是导致我出问题的原因
可以看出来这个数据是随机出来的
这位同学大概没有看我的代码
如果看了 只要几行就够了而不是几百行

img

img

我在MyNetwork 中使用 判断 异常值-1 表示id值的有效性
但是实际上 id 本就是在int范围内 完全可能是-1

img

img

增加对异常情况的判断即可

这个bug 虽然是黑箱测试出来的 但比较适合静态分析和白箱测试(前提是头脑清醒能发现)

Junit测试

通过JML规格构建Junit测试比较清晰,只需将JML规格保证不变的内容全部检查一遍,并检验结果是否正确即可。
比如文档中有提示 检查 requires ensures assignable 等行为

学习体会

这个单元总体来说工作量不是很大
如果不用写单元测试)
这几次的JML体验感说实话不是特别好

  • JML中的嵌套层数真的比较多 再算上量词 很容易出错
  • 即使读懂了JML发现为了保证性能不能完全照着写

但是 我认为这个单元想要传达的是一种契约式编程的思想
正是有着JML这种工具 才能让课程组不用写大段的自然语言来告诉大家每个函数如何写
这个单元大部分的方法都是固定的 而不是自由发挥的 这是与前两个单元的不同
前两个单元是一种黑箱式的契约 告诉你这个程序需要保证正确
然后实现是自由的
这个单元在固定框架(图结构和类、方法) 上如果不用JML
就需要写更长的文档 说明每一个方法应该如何实现和边界情况
同时自然语言又很容易导致歧义 虽然JML没能完全避免这一点
但总的来说 JML提供了一个范例和可行的方案

还有一点 规格和实现分离的想法也很重要
如果没领会到这一点 就会像我一样在第一次提交的前几个小时学习并查集

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

301

社区成员

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

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