北航OO第三单元总结

北航OO第三单元总结

前言

本单元作业为jml规格的学习与应用,要求我们在阅读jml规格之后实现出完全符合规格要求的方法。当然在完成作业之前我又需要从0开始学习jml规格,但好在jml的入门难度并不大,短短几个小时后就可以读懂作业中的jml规格了。而其实规格就像是一个指导你具体实现方法的说明书,它会为你指定这个方法的前置后置条件,指定完成怎么样的操作,因此其实本单元作业就是跟着jml的说明来写代码,因此其实三次作业我完成的都挺快的,难度主要在于需要高级算法来避免卡时间(不然像就我偷懒导致强测被淦碎(雾))。

规格实现的策略

我在实现规格时总体采用的策略是从上到下,从整体到个体的思路。

  1. 首先整体浏览每个规格,不求完全看懂看仔细,但必须知道每个规格需要干什么,会对容器进行怎么样的操作。因为在实现规格时,选择适用的容器是非常重要的,合适的容器能够大大简化我们实现规格时的写代码难度与可读性,因此我认为在具体上手下代码之前就确定下来使用什么容器是最好的。
  2. 第二步我会先实现一些pure方法,这些方法一般逻辑简单,也没有副作用,先实现比较好。
  3. 最后啃难骨头,对于规格复杂,需要算法优化的方法,就需要仔细看仔细读jml,确保自己写出来的代码符合规格的所有要求。我一般的方法是对每条jml语言用自己的语言翻译一遍,完成代码后对着我翻译的语言一条条对比,看看是否符合要求,不然的话以我鱼的记忆总是刚看完理解玩这条规格就忘记了上一条,很痛苦。
一些注意点
  1. 其实看方法名也是一个提供思路的重要方法,一般方法的名字会最简明扼要的提示这个方法干了什么事情,可不是什么邪道(笑)。
  2. 每一次作业都要仔细对比规格发生了什么变化,可不一定是只有新增的规格,也会有前面规格的修改,还记得第三次作业的jml中前面作业的sendmessage和addmessage方法发生了变化,但是藏在茫茫绿色中根本看不见,最后还是中测bug才让我找出来,好险。

设计测试的方法与策略

我的测试一般会以功能性测试-正确性测试-性能测试的顺序进行。

1. 阅读代码

首先当然是阅读自己的代码咯,阅读代码是性价比很高的debug方式,一般也的确可以发现自己一些非常明显的错误,另一方面也可以让自己更加熟悉自己的代码,也更加熟悉jml规格,非常值得。

2. 功能性测试

设计一些简单的数据,来测试自己的代码能不能正常运行。主要要测试一些规格比较复杂的方法,特别是自己使用了其他算法进行优化的方法,这些方法的代码一般写的时候没有完全跟着规格的脚步走,所以极有可能是会有错误的或者不能跑起来的。在这一步测试中力求能够将自己代码所有的RE错误都找到,能够让自己的代码运行起来,正常的执行功能。

3. 正确性测试

在功能性测试之后,就需要加强自己的测试数据来测试正确性了,最好的方法就是扩大测试数据量,覆盖性测试,随机测试。尽量自己构造一些测试数据,不要完全依靠评测机,这次评测机弱中测的数据及其简单,一堆bug都可以通过评测,很多依靠评测机测试的同学都吃了大亏,(我也是呜呜呜)。

4. 性能测试

主要是优化时间复杂度,可以看代码找出一些自己时间复杂度高的方法,或者拿极限数据爆一下,看看是否会超时等等。

容器的选择与使用经验

容器的选择试来试去还是老三样最好:
ArrayList HashMap HasheSet
可以根据功能需要与容器的特点来选择合适的容器。

ArrayList:一般的数组结构,在遍历操作较多的时候推荐这个容器。

HashMap:在需要根据对应关系快速查找时,HashMap是最好的选择,例如本单元作业中Person,Group,Message都有其独一无二的ID,则根据ID的对应关系建立HashMap容器,在使用时就可以非常方便地查找。

Hashset:处理集合关系时使用Hashset比较好。

实际写代码时这三种容器足以瞒住我们的所有要求了,特别是HashMap容器还有很多妙用,例如在第三次作业中实现的emojiMessage的相关方法,规格中要求建立了EmojiIdList和EmojiHeatList两个数组,但是在阅读规格后就会发现,这两个数组的行为全程完全一致,完全可以用一个IdToHeat的对应关系使用一个HashMap储存,在新增删除操作时也只要操作一次,方便很多,错误可能也更低。
(注意事项:使用HashMap和HashSet若需要遍历,建议使用迭代器来操作,一方面效率较高,另一方面也可以避免一些错误。)

性能问题分析

本单元作业还是有很多设计性能问题的方法的,例如

  • 第一次作业中的isCirclequeryBlockSum方法
  • 第二次作业中的queryGroupValueSum方法
  • 第三次作业中的sendIndirectMessage方法

前两次作业的方法都是查询方法,因此我采用空间换时间的方法来降低时间复杂度,提前存好需要查询的变量,在每次涉及这些变量的改动时,及时修改,这样下来查询时直接返回就可以了。
然而第三次作业的求最短路径方法显然无法采取这种策略,因为要储存的实在太多,每一个都计算并储存反而麻烦,这种情况下只能从算法的角度解决。第三次作业时我采用了O(2*n^2)的迪杰斯特拉算法,结果还是超时了,事后询问同学才知道还可以使用堆优化来降低常数量。

架构分析

image
架构其实没啥好说的,按照规格来就完事了。
当然为了算法实现的方便,我自己新增了不少方法。

感想体会

学习JML(×)
练习算法(√)
本单元作业其实还蛮有趣的,在学习了JML的语法后,跟着规格一步步来写代码也是一种船新的体验,有一种玩拼图游戏的爽感,把每个方法都实现最后一下子跑起来的感觉就像把拼图拼完了一样。三次作业下来,阅读规格的速度也越来越快了,对于不同的规格,也学会了选择合适的容器,选择合适的方法来实现,总体来说对我也是有不小提升的。
但是最后都卷入卡时间写算法的痛苦中了,互测强测被各种大数据卡爆,就很痛苦(

posted @ 2021-05-29 11:14  kiasama  阅读(48)  评论(0编辑  收藏  举报