BUAA_OO第三单元总结

高悠然-22371242 学生 2024-05-18 20:02:43

OO第三单元总结

1、测试过程

1.1、黑箱与白箱

黑箱测试:要求测试人员站在用户的角度进行测试,给出数据输入,只要代码的输出合理即可,不需要关注内部的细节与实现。这种方法通常用于功能测试与用户角度的测试。我们平常debug的方法就是这种,我们的公测与互测其实也是黑盒测试。
白箱测试:需要测试人员去检查代码内部的结构有与逻辑,通过代码分析,来保证自己的数据覆盖全部情况,无逻辑错误。主要用于单元测试与集成测试。

本单元中我们两种方式都有使用,黑箱测试我们一直在使用,对拍等,在编写junit测试时,对着JML的要求与逻辑,对每个情况进行测试便是白箱测试。

1.2、多种测试

  • 单元测试:是针对代码中的最小单元进行测试,通常是函数或方法,这种测试是基本单元的测试,防止在函数实现时产生问题;
  • 功能测试:是验证系统功能是否符合需求和规格,这种测试可以帮助我们检查是否会遗漏功能,比如这单元JML功能繁多,很容易遗忘,这个测试提供了保证;
  • 集成测试:是验证多个模块之间的交互和集成是否正常,这个测试是在单元测试的基础上,保证接口之间连接等没有错误;
  • 压力测试:是测试系统在负载情况下的性能表现,这次我们的强测中就有很多对于性能的测试,它可以让我们发现可优化的潜力;
  • 回归测试:是在修改代码或添加新功能后,重新运行之前的测试用例,确保修改不会影响原有功能的正常运行,在我们修复bug后,要保证原有的正确功能不出问题。

1.3、数据构造

  • 单指令构造,对于代码功能检查,类似于白箱测试。
  • 随机化方法对拍,采用黑箱测试,用数据生成器生成数据点,与他人对拍
  • 压力测试,大数据测试,根据要求上限,构造较为 复杂的网络,比如qtvs的复杂度较高,增加其指令数,检测潜在的性能问题。

2、架构分析

2.1、我的架构

img

因为本次基本都是官方的架构,所以无需改变,这里不再赘述,主要是对于该网络结构进行一个解释

2.2、网络结构

这次整个单元的要求其实就是维护一个网络结构,人是每个网络的节点,边就是人们之间的关系,只有有联系的人们之间才有边。
网络构建

  • 因为我们的网络中,tag和人都有独一无二的id,因此不难想到,hashmap是一个很好的选择,以id作为key,不过需要注意的是,用hashmap的初衷之一是出于速度的考虑,有的容器可能需要一直遍历,使用arrylist显然更加好。通过hashmap以及Acquaintance的连接,当有人有关系,便会加入act中,实现点与点之间的连接。
  • Tag的作用像是一个分组或是标签,将一类人划分为一个tag
  • message就是我们平常所发送的消息,只有有联系的人之间才可以发送消息。

维护策略
本单元的难度除了读JML外,还有就是对于整个网络的维护,由于时间限制的存在,我们尽可能免去不必要的操作,我主要实现了以下几个维护方式:

  • 并查集修改:因为需要查找连通性,但每次查找都去重新计算,深度较高的网络会有很大的负担,因此我们需要动态的去维护,由于连通只要存在路径即可,因此中间的过程不需要在意,只要存下来是否有联系,我们可以选择一个连通分量的一个点为中间联系点,只要和这个点有联系的点都是联通的,我采用了以下的方式:

    public Person findFather(Person p) {
          Person father = p;
          while (personNet.get(father) != null) {
              father = personNet.get(father);
          }
          if (!father.equals(p)) {
              personNet.put(p,father);
          }
          return father;
      }
    

    这个函数函数会返回联系点,同时重新构建。因为我们涉及到删除边的操作,但删除边,无非是一个连通分量变成两个或者仍是一个,只要针对删的节点进行局部重建即可。

  • 三角形查询:我采用的是动态维护,因为查询时遍历找三角形十分复杂,我会在增加边或者删除边时动态更新三角形的个数,只用查找边的两个节点之间是否存在连接点即可。

  • qtvs指令:我采用的仍是动态查询,设计了标志位,只有在删除了边或者增加了边时才会对vs进行重新计算,这样可以避免重复的计算。且因为我束缚于JML的实现,使用了近乎n^3的复杂度,其实转换一种查询方式,可以只有n^2,即优先遍历tag每个人的邻居是否在tag中,这样即使不动态维护也能满足课程组要求。

综上不难发现,我们基本采用的是动态维护策略,这种策略的核心思想其实将这条指令所承担的代价分散给其他指令,达到一种均衡的效果,或者有的方法将其分散计算比一起起算的代价要更小。

3、bug与修复

3.1、出现的bug与修复

我主要是第二次作业的强测中有一个点时间性能爆炸了,因为当时并未对qtvs进行动态维护,且由于个人实现问题,出现了n^3的复杂度。我后面实现了动态维护,主要是在加边和删边以及更改value时进行局部更新,同时不更新时返回以前的值,这样可以避免大量无意义计算。

3.2、规格与实现

对着JML写代码,这个我觉得有点像黑箱测试,就是我们满足了JML对于前提和结果的约束,但中间的过程我们可以自己实现。这也是我们一直强调的规格与实现分离。
比如在类的属性时,使用数组表示,我们可以改成容器,甚至可以用不同的存储内容,在满足规格的基础上,要便于我们实现。
在撰写方法时,我一开始容易按照JML一条一条写,因为这可以保证我们的正确性,但这大大提高了了代码的冗杂度,以及降低可读性,后面我会先根据JML了解哪些实现,使用自己的方法来实现,而且有些地方我们的性能有一定要求,所以规格与实现分离是必要的,JML的规格往往会比较冗长。

4、Junit

4.1、Junit测试编写

相较于编写代码,JML规格对于Junit测试的作用我个人感觉更大,因为JML会给出相应的变量改变以及返回值要求,我们只要在junit对于JML每条ensure进行检查即可,有一个需要注意的点就是pure!
在造数据时,我并不是用随机化,感觉过于冗杂,采用的是白箱测试,注重于逻辑使用,因为我们的方法其实结果是可数的,针对每种情况构造即可,每次构造出的图其实也没有怎么复杂,一般几个点就可以。那最后一次作业举例,他要保证message不被改变,但message里不止一种message,也不止一个属性,为了防止他们其中一种被改变,我们必须构造出所有的消息类型。

4.2、规格与Junit实现

我们的JML中会把所有需要检测的点告诉我们,我们的junit是为了保证方法的正确实现,所以我们只要保证了JML的ensure均被实现即可,也就是在我们的Junit中实现,是规格与实现的一致性。

5、心得体会

这个单元总体难度较为简单,根据JML实现代码的过程比较复杂,因为JML不是自己写的,所以相当于读一份冗长的代码,又要自己写一份。
不过这个单元也是学到了一些新的性能处理方法,比如动态维护与并查集等,本来以为只要简单的抄写JML就行,其实还是有很多细节在里面。
最后是我个人的一些想法,Junit与JML很消磨时间,而且JML也一直受诟病,对于JML的作用,在写Junit的时候体现的比较好,但对于实际的项目,还是没有体会到优势,觉得课程组可以对这方面进行一定改革。

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

301

社区成员

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

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