Unit3总结——规格化设计

申剑璋-22373372 学生 2024-05-16 20:36:16

引言

第三单元作业的主题是“规格化设计”,要求我们对JML语言有所理解。并且可以基于JML语言完成代码的开发。JML语言通常运用于对程序正确性、稳定性有极高要求的场景下,因此本单元的难点也就在于理解JML语言的逻辑思路,以及如何将其正确的“翻译”成我们所能够理解的自然语言。但是JML仅仅只是作为一种契约规范程序的行为,针对同一种规格化方法有不同的实现方式,故在编写代码的过程中运行时间也是我们需要重点考虑的问题(否则会造成TLE*)。

测试过程

黑箱测试

  • 对于黑箱测试而言,程序内部的实现逻辑并非其关心的重点。黑箱测试更关注的是程序所能够实现的功能,检查程序是否能够按照规格所说明的要求正常工作。
  • 黑箱测试的优势如下:
    • 实现简单,不需要了解程序的内部实现。
    • 从用户的角度出发,能够更清楚的了解程序的功能,以及其运行过程中可能遇到的问题。
    • 更容易完成软件的自动化测试。
  • 但是其缺陷也很明显:
    • 不可能覆盖所有的代码,覆盖率较低。
    • 自动化测试的复用性较低。
  • 在第三单元的Junit自动化测试和互测环节中,我发现通过黑箱测试的方法确实能够很好地理解程序中每一个函数的功能,并且通过构造充足的数据也能够找出程序中的功能问题。但是黑箱测试的问题就在于不了解代码的内部实现,没有办法实现对代码的针对性测试。难以检测出程序中RTLECTLE等问题。

白箱测试

  • 白盒测试也称为结构测试,主要用于检测软件编码过程中的错误。程序员的编程经验、对编程软件的掌握程度、工作状态等因素都会影响到编程质量,导致代码错误。白箱测试则要求了解程序内部的具体实现,按照程序内部的结构测试程序,检验程序中的每条通路是否都有能按预定要求正确工作,而不顾它的功能。
  • 白箱测试的优势如下:
    • 帮助软件测试人员增大代码的覆盖率,提高代码的质量。
    • 发现代码中隐藏的问题。
  • 但是白箱测试也存在缺陷:
    • 测试基于代码,只能测试开发人员做的对不对,而不能知道设计的正确与否,可能会漏掉一些功能需求。
    • 系统庞大时,测试开销会非常大。
  • 我认为借助白箱测试能更好地挖掘出程序所存在的潜在问题,因为白箱测试需要了解代码的内部设计逻辑,通过对代码进一步的阅读理解能够构造出更具针对性的数据进行测试,同时还能够最大程度地实现代码的测试覆盖。

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

  • 单元测试是针对软件的最小功能模块进行测试的方法。通过完成单元测试,能够最大程度上保障代码模块功能的正确性。本单元的Junit测试就是单元测试的一种。
  • 功能测试是对整个软件系统的功能进行测试的方法。在功能测试中,测试人员借助规格说明书来构造数据以此来检查软件设计是否满足需求。功能测试的目的就是确保软件能够按照用户的要求正常运行,并能够正确地处理各种情况。
  • 集成测试是在单元测试的基础上将各个模块功能组合起来进行测试。集成测试的目的就是为了保证模块之间接口与交互的正确性,并且整个软件系统能够正常协作,模块间彼此不会产生冲突。
  • 压力测试,顾名思义就是对程序在高负载和大并发情况下进行测试的方法。其测试目的就是找出系统所能承受的数据规模以及性能的极限,以此来评估程序的性能和稳定性。在本单元的测试中,借助压力测试的思想构造出极端数据,能够有效地找出程序所存在的性能问题,例如TLE
  • 回归测试是软件系统在完成修改和迭代后,重新运行之前的测试数据以验证修改和迭代是否引入新的错误或导致原有功能出现问题的方法。回归测试的目的是确保在对软件进行修改和升级后,系统原有的功能和性能没有受到影响。在本单元中借助回归测试能够有效地减轻debug的压力——只需要检查新增部分代码的正确性即可。

数据构造

  • 在本单元的数据构造中,随机生成数据这一策略很难满足强测的测试要求。首先,随机生成的数据可能并不满足正确性要求,因为此次数据需要满足图的合理性。其次,随机生成的数据可能会导致出现大量的孤立点,所生成的图关系太弱,难以满足测试要求。故在本单元的数据构造中,我们需要借助图的一些相关算法以此来生成复杂度较高的图,通过这种方式就能够提高测试的强度。
  • 除此之外,将测试数据的重点集中在代码中时间复杂度较高的方法,也能够有效提高数据测试的强度,以此来减少代码出现bug的可能。

架构设计与维护

img

在本次作业中,我们需要实现的是一个社交关系网络——MyNetwork。社交网络中包含的是MyPerson,MyMessage,以及人与人之间的关系。简而言之,最终所构造出的就是一个有权无向图。

  • MyNetwork作为无向图的载体,构建了整个无向图。其中MyPerson是图中的节点,MessageTag等信息都是MyPerson中的属性。图的连接借助了MyPerson中的Acquaintance,当person1的Acquaintance中出现person2则表明两者是有联系的。
  • 对于图的维护策略主要集中在三个方面:
    • 首先就是对于并查集的维护,在第一次作业中isCircle()用于检查两个人之间是否有联系,为了减少查询的时间复杂度,我们采用了并查集将同一连通分支的父节点设置为同一个节点,这样处理就能在最大程度上减少查询时的时间复杂度。但是当原有关系出现变化时,例如增加、修改关系,这都会造成原有的并查集发生混乱。因此,当关系发生变化时,我们需要利用DFS算法将并查集进行更新。
    • 其次,就是对BestAcquiantance的查询,以及对coupleSum的计算。如果按照JML规范中的步骤进行查询,时间复杂度会相当高,强测一定会出现TLE的问题。因此,我选择对BestAcquaintance进行实施维护——因为其值发生改变只可能是在addRelation,ModifyRalation时发生变化。而当BestAcquaintance得到了实施维护,那么对coupleSum的计算只需要复杂度为o(n)的算法即可。
    • 最后就是对TagvalueSum的计算,如果不进行任何优化,则该计算的复杂度会是o(n^2),会导致超时。因此,同样的也是采用的动态维护的方法,其实现思想与BestAcquaintance的维护差不多。

规格与实现分离

  • 规格的目的只是为了准确地表述出相关方法的功能,并没有考虑方法实现的时间复杂度和空间复杂度。如果按照规格的描述来实现对应的方法,那么就有可能会导致TLE等问题。因此,在具体实现过程中,我们只是借助规格了解所实现的功能,对于功能实现的效率、可维护性等需要我们融入自己思考。
  • 规格与实现分离可以提高抽象层次,可以专注于“做什么”而不是“怎么做”,这有助于清晰地理解问题。同时可以更容易地在不同模块间共享接口。测试可以针对规格进行,而不必关心实现细节,这有助于提高测试的有效性和效率。

Junit测试

在本单元的Junit测试中,我们可以利用已经提供的JML来进行编写测试。通过确保JML规格中的require,ensures,pure等,我们就能够较好地完成最后的正确性测试。同时借助JML来进行测试也能够加深我们对于契约式编程的理解。
在构造Junit测试数据时,我采取的方式就是一力降十会,例如构造出一点一点构造出完全图,并且在构造的过程不停地对相关方法进行测试。

学习体会

本单元作业的难度显然是不如前两个单元的,整体下来所需要投入的时间也不算多,但是仔细认真地阅读JML还是很重要的(否则就是像我一样犯一些愚蠢的错误)。在我看来,JML的优势就在于逻辑严谨且表述精确,但是其可读性却是远远不如自然语言。
通过这一单元的学习,我基本上掌握了利用JML语言进行项目开发,学会了JML语言的阅读和基本的编写,并且对如何进行项目测试有了更加深入的了解。

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

301

社区成员

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

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