BUAA-OO-2024-第三单元总结

孙锐毅-22373500 学生 2024-05-16 22:01:37

目录

  • OO第三单元总结
  • 第九次作业
  • 架构设计
  • 图模型构建和维护策略
  • 性能问题及其修复情况
  • Junit测试方法
  • 第十次作业
  • 架构设计
  • 图模型构建和维护策略
  • 性能问题及其修复情况
  • Junit测试方法
  • 第十一次作业
  • 架构设计
  • 图模型构建和维护策略
  • 性能问题及其修复情况
  • 其他bug分析
  • Junit测试方法
  • 对规格与实现分离的理解
  • Junit测试方法
  • 分析本单元的测试过程
  • 对黑箱测试、白箱测试的理解
  • 对单元测试、功能测试、集成测试、压力测试、回归测试的理解
  • 数据构造有何策略
  • 本单元学习体会

OO第三单元总结

第九次作业

架构设计

img

图模型构建和维护策略

第一次作业的图还较为简单。MyNetwork为主图,其中每个MyPerson为一个节点,PersonPerson之间的relation为边。每个MyPerson中记录了顶点的必要信息,包括id,name,age这些基本信息,以及一个熟人容器acquaintance,即相邻的节点,确保能够根据此找到点与点之间的联系,同时还有一个value,记录与熟人之间的关系值。

总的来说,图并不复杂,由于每个人都有唯一的id,数据容器方面选择了Hashmap,能够便于访问

同时,在这一次作业中的方法也都与图有关

方法名称作用
addRelationNetwork中添加一条边,即在图中增添一条边
modifyRelation修改两个Person的关系,带来的结果是value值的改变。且根据value的值,两个人之间的关系可能发生变化,即可能会导致图中边的删除
queryValue获得两个人之间的关系值
isCircle判断两个人是否有关系,即判断在Network图中两者是否连通
queryBlockSum即计算连通块个数
queryTripleSum计算三元环个数

前三个方法的实现都较为简单。

后三个方法的实现,我借助了并查集算法和动态维护。

isCircle方法只需要判断两个人在并查集中的根节点是否相同即可,若相同则说明两者连通,否则未连通。

queryBlockSum方法的实现,我则是动态维护了一个blocksum,当对Network图进行增删边操作时,相应的对blocksum进行修改,保证其正确性即可。

queryTripleSum方法的实现,我也是使用动态维护一个triplesum,在对Network图进行增删边操作时,遍历图的顶点,找到与该边的两个顶点都相连的点,若存在这样的点,则对triplesum做出修改。

性能问题及其修复情况

在本次作业中并未出现性能问题,也没有出现bug,得益于数据容器的正确选择以及算法层面的优化,如果按照规格写的用两层for循环操作的话,在时间层面的优化效果应该会大打折扣。

Junit测试方法

本次作业中测试的方法是queryTripleSum,这个方法关注的点是Person以及Person间的关系。

首先是在数据构造方面,要考虑所构造的图的特征,应包含有稀疏图与稠密图等,即根据Person的数量,增添的relation也应该在一定的范围中。同时,如果规格有对前置条件的要求,构造的数据应该满足这个要求,不然用不满足要求的数据来测试方法是没有意义的,因为其不能保证正确性。

在测试方法的实现中,Junit测试需要对规格的直接翻译,确保能够准确按照规格的释义来完成方法的构建。queryTripleSum的规格中是一个三层循环来完成,相应的我们也因用三层循环来检验。

最后是正确性判断,主要是根据规格的ensurepure的要求。对于每一个ensure,我们都需要构建相应的检测语句对这个确保满足的条件进行检验,对于pure,我们要检验前后是否有对象的值发生改变。对于queryTripleSumensure确保的是结果满足的条件,将三层循环得到的结果与方法结果比较即可;同时,其被pure修饰,需要检查方法前后,person[]的值是否发生改变。

第十次作业

架构设计

img

图模型构建和维护策略

这次作业变化最大的点是加入了Tag,相当于一个分组,这次作业新增的方法也都是围绕着这个Tag展开。

Tag中有一个Person[],以及围绕着这个容器的方法。

Network中新加的方法如下

方法名称作用
addTag为指定的Person加入一个Tag
addPersonToTag向指定的Person的指定Tag加入一个Person
queryTagValueSum获得指定的Person的指定Tag中的ValueSum(与TaggetValueSum相关)
queryTagAgeVar获得指定的Person的指定Tag中的AgeVar(与TaggetAgeVar相关)
delPersonFromTag删除指定的Person的指定Tag中的指定Person
delTag删除指定的Person的指定Tag
queryBestAcquaintance获得指定idperson的最好熟人(即找到关系值最大的熟人)
queryCoupleSum计算当前网络中互为最好熟人的对数
queryShortestPath计算两个人之间的最短路径

这次作业中新增的方法中,以qurey打头的几个方法是实现重点,其中大多数我都使用了动态规划。

对于queryTagValueSum方法,我是对于每个Tag动态维护了一个valuesum,当向这个Tag中增添Person或者删除Person时,对valuesum的值进行修改。同时,当有两个Person的关系的value被修改时,需要遍历所有的Tag,将含有这两个PersonTagvaluesum进行相应的修改.

对于queryTagAgeVar方法,同样是对每个Tag动态维护了一个agevar,当向这个Tag中增添Person或者删除Person时,对agevar的值进行修改.

对于queryBestAcquaintance方法,我对于每个Person动态维护了一个bestid,对于bestid的修改分为两种,一种是向Person中新增关系或者修改不为与bestid的人的关系时,需要将新增的人或者修改的人与bestid的人的关系进行比较修改;另一种是在besid改变时(修改或者删除),需要重新寻找新的(即原第二大的)bestid。这个可以借助优先队列实现(但我没有完成这一点)。

对于queryCoupleSum方法,我则是在Network中动态维护了一个couplesum,当增添关系或者修改关系时,对couplesum进行维护。

对于queryShortestPath方法,因为对于最短路径的动态维护成本较高,我则是采取在查询时计算的策略。最短路径的算法很多,最开始我选择的是Dijkstra算法,后来强测爆掉了一个点也是因为这里,后来改成了bfs

性能问题及其修复情况

这次作业只有强测出现了一个bug,问题就是出在queryShortestPath方法的算法选择上面,最开始我选用的是Dijkstra算法,后来改成了bfs。由于在这次作业中,每个边的权值是一样的,使用Dijkstra算法会格外存储许多非必要的信息,相比之下,对于无权图,使用bfs的效率要高上许多。

Junit测试方法

本次作业测试的方法是queryCoupleSum,这个方法关注的重点在于acquaintance这个容器。

在数据构造方面,由于没有提供对于acquaintance的返回方法,因此针对每一个构造的Network,在生成的同时还需要生成一个相应的acquaintance来满足后续的测试需求。同样的,我们仍然需要考虑所构造的图的特征,应包含有稀疏图与稠密图等,即根据Person的数量,增添的relation也应该在一定的范围中。本次规格没有对前置条件的要求,数据构造没有这方面的限制。

在测试方法的实现中,这次的翻译有所不同,因为在这次的规格实现中,使用了queryBestAcquaintance方法,我们需要将其实现(如果不保证queryBestAcquaintance方法的正确性时),即将queryBestAcquaintance换为其规格翻译。

最后是正确性判断,对于queryCoupleSumensure确保的是结果满足的条件,将按照规格计算得到的结果与方法结果比较即可;同时,其被pure修饰,需要检查方法前后,person[]的值是否发生改变。

第十一次作业

架构设计

img

图模型构建和维护策略

本次作业新增了Message及其相关的三个类EmojiMessage,NoticeMessage,RedEnvelopeMessage,即最后一次作业的实现是完成了我们社交网络的发消息功能。

Person中新增了收到消息容器ArrayList<Message> recivedmessages与社交值SocialValue,钱money.

Network中新增了存储消息的Message[] messages容器与emojiIdList,emojiHeatList两个与emoji有关的int [],新增的方法如下

方法名称作用
containsMessage检查messgaes中是否含有指定idmessage
addMessagemessages中加入message
getMessagemessages中得到指定idmessage
sendMessagemessages中发送指定idmessage
querySocialValue获得指定idPerson的社交值SocialValue
queryReceivedMessages获得指定idPerson的收到消息recivedmessages
containsEmojiId检查Network中是否含有指定idemoji
storeEmojiIdNetwork中加入指定idemoji
queryMoney获得指定idPersonmoney
queryPopularity获得指定idemojiPopularity,即相应的emojiHeatList中的值
deleteColdEmoji删除NetworkemojiHeatList值不满足要求的对应emoji
clearNotices清除NoticeMessage

性能问题及其修复情况

这次作业中对性能的考察不大,更多的考察是对规格的理解与实现。

其他bug分析

本次作业也是在强测出现了bug,其原因也是对规格的实现上出现了问题,没有按照规格的要求进行“翻译”,做了一定的简化处理,但简化处理的实现逻辑与规格有一定的出入,从而导致出现了问题。具体来说就是在对money的处理中,规格中是将money先除以peoplo_size后再乘以people_size,而我直接将其简化,并未先除再乘,从而因为int类型的特性导致两者的结果其实并不相等。

Junit测试方法

本次作业测试的方法是deleteColdEmoji,其重点关注的是两个与emoji有关的容器emojiIdList,emojiHeatList,以及消息容器messages

因此在数据构造方面,我们这次构造的数据需要重点考虑messages的构造。首先message的种类有三种,每种message又有两种类型,因此在构造message时要充分考虑每一种可能性,确保数据的覆盖率。同时每种消息的数量和消息中所对应的emoji,tag等数据的大小也得在合适的范围,避免出现测试强度低,数据覆盖不完全的情况。

测试方法的实现,也是老老实实按照规格进行翻译。

最后是正确性判断,这次的deleteColdEmoji方法所对应的ensure较多,有6个,对于每个ensure我们都应该进行相应的正确性检查,其中包括了对改变后的emojiIdList,emojiHeatList,messages的相应变化。对于emojiIdList,emojiHeatList的变化只与limit有关,对于messages中的每个message,根据其类型的不同,做出的改变也不同,需要分别进行判断。

对规格与实现分离的理解

规格与实现分离顾名思义就是将代码的规格与他具体的实现分割开,通俗一点来讲就是,代码的实现并不一定需要照着规格一板一眼的直接翻译,而只需要满足规格所给定的要求,不限制具体的实现方式。

这么做固然带来了许多的好处。首先,这样讲规格与实现独立开来,规格定义了系统应该做什么,而实现则决定了如何做。这样,当需求变化时,只需要修改规格而不影响实现,当需要对系统进行维护或修改时,可以专注于修改实现细节,而不必考虑规格的改变。同时,这样十分方便测试,规格定义了系统的功能和行为,可以作为测试的基础,而实现则是根据规格进行开发和优化,测试其确保符合规格的要求。当然,规格与实现分离也有助于提高代码的可复用性。通过将规格定义清晰,可以将其作为接口或约定,使得不同的实现可以在不同的系统中复用。

Junit测试方法

Junit测试对规格信息的利用主要是三个方面,数据的构造,测试的实现,正确性的检验。

对于数据构造,通过规格我们可以明白该方法处理的前置条件,根据其我们可以构造出满足要求的各式各样的数据,也可以进行边界测试与异常测试等。对于测试的实现,我们要检验一个方法的正确性,需要依照规格写出直接翻译的实现,保证完全的正确性,从而能够作为我们方法实现的参照。而最后正确性的检验则是看修改内容与后置条件,我们修改的内容是否符合规格的规定,我们最后给出的结果是否满足规格给出的要求,这些内容都需要我们进行检验。

毫无疑问,Junit测试检验代码实现与规格的一致性的效果是突出的,但这建立在我们构造的测试具有完备性的基础之上。只有我们的测试数据覆盖的范围全面,才能够检查出所有可能存在的问题。这也是对我们编写Junit测试能力的考验

分析本单元的测试过程

对黑箱测试、白箱测试的理解

黑箱测试(Black-box Testing):在黑箱测试中,测试者对被测试系统的内部结构和实现细节一无所知。测试者只关注系统的输入和输出,通过给定的输入数据,验证系统是否能够正确产生预期的输出结果。黑箱测试侧重于测试系统的功能和行为,而不涉及内部的实现细节。它类似于将系统视为一个黑盒子,只对外部可见的接口和功能进行测试。

白箱测试(White-box Testing):在白箱测试中,测试者对被测试系统的内部结构和实现细节有详细的了解。测试者可以查看系统的源代码、算法、数据结构等信息,并基于这些信息设计测试用例。白箱测试主要关注系统的逻辑完整性、覆盖率和性能等方面,通过深入了解系统内部的实现细节来设计有效的测试用例。

老师一直强调我们既要有黑箱测试,又要有白箱测试。对于黑箱测试,我们常是用一大堆数据测试,有时候不清楚所给的测试数据的覆盖面如何,也许我们测试1万组,甚至更多,也无法保证测试完全了所有的情况,虽然这确实能够较好的检验我们所写程序的正确性。白箱测试我们更多的是分析代码的实现细节,构造特定的测试样例对程序进行检查,更加具有针对性,也能够弥补一些纯靠黑箱测试无法发现的问题。两个测试互为补充,缺一不可,将他们结合起来是对我们程序正确性判断的一个重要途径。

对单元测试、功能测试、集成测试、压力测试、回归测试的理解

  • 单元测试(Unit Testing):单元测试是针对软件中最小的可测试单元,即代码中的函数或方法进行的测试。它通常由开发人员编写,并用于验证代码的正确性和可靠性。单元测试可以帮助开发人员及时发现和解决代码中的问题,从而提高代码质量和可维护性。
  • 功能测试(Functional Testing):功能测试是针对软件特定功能的测试,旨在验证该功能是否按照需求规格书中所述的要求正常工作。它涉及对软件的输入、输出和行为进行测试,并检查是否符合预期结果。功能测试通常由测试团队执行,以确保软件符合用户的需求和期望。
  • 集成测试(Integration Testing):集成测试是将不同的模块和组件集成到一起,并测试它们之间的交互和协作。它旨在验证系统的各个部分能够协同工作并产生正确的结果,以确保整个系统的正确性和稳定性。
  • 压力测试(Load Testing):压力测试是通过模拟大量的并发用户和数据来测试系统的性能和稳定性。它旨在验证系统的负载容量和响应速度,并确定系统在负载情况下的最大承受能力。
  • 回归测试(Regression Testing):回归测试是在软件发生变化后重新运行之前的测试用例,以确保已有的功能仍然正常工作。它有助于验证代码更改是否影响了现有功能,并尽早发现和解决问题。

这些测试都能够用于我们的检测之中。

数据构造有何策略

本单元的数据构造策略有两个,一个是功能测试,一个是压力测试,其具体内容如上。主要就是想要考虑在功能复杂和指令数量增多后程序的处理情况。

本单元学习体会

这单元的内容相较于前两单元,给了人一种不同的感受,尤其是在多线程单元之后。

第三单元的主题是JML规格,是我第一次接触这个内容。一开始的时候,我确实觉得编写规格有些多此一举了,明明自然语言一两句话就能描述清楚的问题,非要用规格来写一长串,读和写的难度都上了一个台阶。既然要多费功夫,自然得有花费带来的好处,自然语言有一个缺点就是不够严谨,规格做到了这点,这也是我们编写规格最主要的目的——力求方法的严谨,从前置条件到后置条件,再到方法过程中改变的对象等等,这些规格中都有体现,也是对我们编写程序的一种约束。由于这些约束,我们对一个方法有了清晰明了的认识。

现在我只是初步体验了规格和契约化设计,还不敢说已经有了多么深刻的体会,但也算是初步体验。说不定以后那次遇上了他,能够重拾这一段懵懂而跌跌撞撞的回忆吧。

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

301

社区成员

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

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