OO

OO Unit3

Posted by Steel Shadow on May 21, 2023

架构设计 图模型构建和维护策略

我的类 UML 图如下。 类图 由于课程组给出的 interface 中已给出清晰明确的工程结构,这里仅仅介绍我额外添加的内容。

  • style
    由于 MyNetwork 方法过多,行数超过 checkStyle 500行限制,我将一些方法从 MyNetWork 提取出来,放在 style 文件夹中,提取方法中,第一个参数为 MyNetWork this。
    • ExtractSendMessage
    • Sum
      用于动态维护 coupleSum tripleSum bestAcquaintance (blockSum 使用并查集单独维护)
    • Test
      okTest
  • tool 这里放了2个工具类。
    • Dijkstra query_least_moment 寻找最短环路时,需要使用 dijkstra 算法。
    • Set2
      我使用并查集维护统计 blockSum。在 addRelation 时, merge 相应结点,并记录添加历史。在删除关系时(modifyRelation的特殊情况),从历史中删除相应的记录,重新由历史构建并查集关系。这过程中,使用 Set2 无序二元集表示历史的一对结点。

我在第一次作业中,为了维护 blockSum 并保持效率,我决定采用并查集,每个 MyPerson 都有属性 MyPerson parent 表示父节点,并设置 size、rank 用于优化并查集效率。
并且同时维护 Person 之间的树形关系,每个 MyPerson 都有一个 HashMap<Myperson, Integer> 用以表示关系值。

在第二次作业中,需要实现 modifyRelation,设计边的权值修改/删除。并差集难以实现边的删除操作,我采用了讨论区的记录历史 history 方法,在删除边时,重新构造并查集部分。

第三次作业的重点是,queryLeastMoment 给定点求最短环路。我使用讨论区的方法,遍历初始点的邻接点,将它们间的边删除,再使用 dijkstra,环路总长为 删除边的长度+dijkstra 。

测试

本单元使用 JML 描述任务需求,内容繁杂,细节极多。
如果需要手动构造数据进行单元测试,十分复杂。

测试方法

  • 黑箱测试
    黑箱测试是仅给出输入,观察输出,不考虑程序内容。

  • 白箱测试
    白盒测试是测试人员要了解程序结构和处理过程,按照程序内部逻辑测试程序,检查程序中的每条通路是否按照预定要求正确工作。如我们的 okTest 就是白箱测试。

  • 单元测试
    是对软件中最小可测试单元(人为规定的最小必测功能模块)进行检查和验证。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

  • 功能测试
    是对软件中最小可测试单元(人为规定的最小必测功能模块)进行检查和验证。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

  • 集成测试
    在单元测试的基础上将所有模块按照要求设计组装成为子系统或系统,进行集成测试。

  • 压力测试
    针对特定系统或是组件,为要确认其稳定性而特意进行的严格测试。会让系统在超过正常使用条件下运作,然后再确认其结果。此处即,数据量极大,测试程序效率及正确性。

  • 回归测试
    回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。

测试工具

在 idea 中,有一款插件 Diffblue Cover 使用 AI 自动生成 JUnit 单元测试。实测使用效果一般,还是需要修改样例,手动构造数据。

在研讨课上,我了解到其他同学使用对拍工具互相交流对拍。
我没有和其他同学对拍,第二次、第三次作业出现了许多细枝末节的错误。

数据构造策略

由于 JML 阅读量较大,任务说明复杂,我丧失了测试的耐心。
我在测试时并没有手动构造许多样例,仅仅对指导书中给出的例子进行修改,进行测试。

性能——规格与实现分离

在第三次作业中,queryLeastMoment 寻找最短环路中,我错了4 5 9 三个强测性能点。
在 Dijkstra 算法中,我优化了计算逻辑,当给定节点找出最短路径时,即退出算法。
这样通过了强测5,但4、9仍然未通过。
在研讨课上,有同学指出,使用优先队列优化并不一定能提高效率,维护优先队列本身存在开销。

规格和实现分离:
在我们的 JML 描述中,均使用 array 表示数据结构,难以表达映射关系,需要使用 2 个 array 累赘冗余地表示映射结构。
这样不仅造成了 JML 的阅读困难,且如果直接按照数组实现,性能也会极低。

在某些查询指令中,规格是静态描述,即每次查询时都重新计算结果,效率较低。实现中,可以使用动态维护的方法,在修改过程中动态维护 blockSum tripleSum coupleSum bestAcquaintance 等。

在第二次和第三次作业中,我对 Group.queryAgeVar 和 redEnvelopMessage 的数字处理产生了疑惑,我以为需要使用浮点数管理,在输出时只显示整数。但评测结果表明,统计完全为整数。导致我在强测中,每一次都会与评测结果相差1。

如果使用动态统计 $E(x^2)$,使用 $D(x)=E(x^2)-E(x)^2$,由于浮点数精度和取整问题,会与评测结果相差1。但是在往届 blog 中,学长表示这么做是正确的,今年加大了评测力度,很难受,居然被卡了这种点。

我以为红包信息的 money 应当全部分配,在出现非整数分配时,评测出错。

OKTest

OKTest 对于每一条 ensure 都作检查,如果需要模拟规格原始容器,编写测试十分繁复。尽管能代码实现与检验规格一致性,但太过于耗时费力。

在第二次的作业中,okTest需要检查 20 余条 ensure ,仅仅 JML 描述就有上百行,我个人在编写 okTest 心情十分糟糕。

本单元学习体会

本单元学习了 JML 语言,我了解了 JML 语言的准确和繁杂,深刻认识到到需求描述准确的重要性。

在评测前,可以与其他同学交流对拍,不能一个人闷头测试。

总之,十分折磨的一单元,总是在细枝末节的错误上踩坑。