BUAA OO第三单元总结

本单元考察了对于JML语言的理解和运用,根据课程组给出的JML的规格实现相应的代码,整体实现内容并不复杂,大多方法只需按照JML照葫芦画瓢,在少部分方法中需要通过选择合适的算法等方式提高性能

本单元测试过程

黑盒测试与白盒测试

黑白盒的概念更多出现在对抗攻防中,黑盒顾名思义,代表的是无法了解内部具体实现方式,只能通过输入输出判断运行的情况,白盒测试则可以详细了解代码内部实现方式,可以针对源码级别的bug进行针对性的攻击和测试

  • 黑盒测试
    在作业当中,我更多采用的是黑盒测试,即采用大量的随机数据,试图覆盖尽可能多的情况,从而测试出某些情况出现的bug,除了通过数量取胜之外,试图构造一些较强的数据点,对于性能进行挑战也是一种有效的黑盒测试方式
  • 白盒测试
    理论上来说,相较于黑盒测试,白盒测试更能够针对性地找出Bug,但是由于要求对进行测试的代码进行整体上的阅读,在实际的hack当中,被采用的更加小众,因为需要花费更多的时间和精力去完整的理解代码内容

单元测试

单元测试用于测试软件的最小可测试单元,通常是代码中的函数或方法。它的目的是测试单元的逻辑正确性和准确性,以确保单元能够按照预期工作。
我们运用junit对代码中的方法进行测试的方式就属于单元测试,也是目前我们主要接触的测试方法之一

功能测试

功能测试用于测试软件的各个功能是否按照预期工作。它的目的是检查软件是否满足用户需求,以确保软件能够按照预期工作。

集成测试

集成测试用于测试多个模块或组件之间的交互是否按照预期工作。它的目的是检查软件模块之间的接口是否按照预期工作,以确保软件能够整体按照预期工作。

压力测试

压力测试用于测试软件在高负载下的表现。它的目的是确定软件在高负载情况下的性能瓶颈和极限,以便为软件性能优化提供指导。

回归测试

回归测试用于测试修改后的软件是否仍然按照预期工作。它的目的是确保软件修改或更新后没有引入新的错误或问题,以便确保软件在不同版本之间的兼容性

数据构造策略

  • 广泛性
    全面性数据点构造,尽量测试到各种不同的情况,提高覆盖率,测试面对不同情况程序给出的结果是否符合预期,通过覆盖性测试保证方法的各个分支方法都能够正确实现
  • 针对性
    针对于某一特殊情况或功能进行针对性测试,如第十次作业强测中的第十个测试点,大量加入qtvs指令,从而针对性测试该方法的性能(我就在这个点爆了ctle)

结构设计

在这里插入图片描述
本次作业整体架构与依据JML实现,没有过多添加新的实现,为了维护Person之间的关系,新建了Graph类,在该类中实现了并查集、压缩路径、最短路径查询等一系列方法,在Graph中维护了记录祖先的数组和邻接表等数据,动态维护Graph中的triNumblockNum数据,在每次添加、删除时进行维护

性能问题

  • 选取合适的容器
    • 尽量采用HashMapHashSet作为容器而避免采用ArrayList,提高在取出存放数据时的效率
    • 由于第三次作业中的getReceivedMessages()方法,要求取出前五条Message,因此在Person中存放Message的容器选用ArrayList,按照顺序存取
    • Network中除了ArrayList之外仍采用HashMap,以ID为key,储存Message
  • 选取合适的存储方法
    针对于deleteColdEmoji()方法需要删除Network中的部分Message,使用该种方式HashMap<Integer,ArrayList<Integer>> emojiId;进行储存,以emojiId作为key,记录下该emojiId对应的Message的Id,在删除时只需要删除对应id的Message,而不需要遍历Network中所有的Message去筛选出需要删除的Message
  • 采用合适的算法
    • 如在第一次作业中使用并查集,维护triNumblockNum,值得一提的是在删除两个节点关系时的方式,首先采用dfs遍历所有与A节点有关联的点,将他们的祖先全部标记为A,若B的祖先为A,则说明A与B仍然相通,则不需要进行额外的操作,否则再对B进行dfs,将与其有关系的节点祖先设为B,blockNum+1
    • 在第二次作业查询最短路径中,由于之前已经在Graph中维护了邻接表,直接采用bfs算法查询即可,相较于dfsbfs更适用于查询最短路径,一旦找到就是最短的
  • 一种奇妙的提升性能算法(针对于qtvs
    Tag中的getValueSum()方法中,我原本采用的结构是
for (MyPerson person1 : persons.values()) {
    for (MyPerson person2 : persons.values()) {
        if (person1.isLinked(person2)) {
            sum += person1.queryValue(person2);
        }
    }
}

然后就被strong10给爆ctle了o(╥﹏╥)o
之后在研究讨论 (看舍友的) 之后,发现了略加修改就可以修复ctle的问题

for (MyPerson person1 : persons.values()) {
    for (MyPerson person2 : person1.getAcquaintance().values()) {
        if (persons.containsKey(person2.getId())) {
            sum += person1.queryValue(person2);
        }
    }
}

由于所有与person1有关系的Person都在acquaintance中,相当于直接把所有满足isLinked()Person直接取出来,减少了需要遍历的长度,降低了时间复杂度,同时不需要进行动态维护就可以蹭过强测的测试点

junit测试

  • 全面性考虑JML的各项要求
    针对JML规格的每一条要求都进行测试,包括前置require,后置ensure,以及对于变量的要求和pure方法的测试,保证测试覆盖JML的每一条语句
  • 设计样例能够覆盖各个分支
    样例应能够充分覆盖到方法的各个分支,从而确保每个分支的正确性,除却充分考虑各种情况之外,也可以采用生成完全图或随机数据的方式进行重复测试,用数量代替质量

总结

本单元整体实现难度相较于前两单元较低,考察的更多是部分算法的性能实现以及能否将JML隐含的一些细节完整的实现
在这里插入图片描述

  • 23
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值