OO第三单元总结(JML规格)
前言
第三单元的作业主要是为了帮助我们了解JML规格的设计和使用,我们需要通过阅读JML规格说明书以及实际的表述来完成我们的代码设计。本单元的主题是实现一个社交关系模拟系统,可以通过各类输入指令来进行数据的增删查改等交互。不同的人通过社交网络建立各自的联系,类似于微信、QQ等软件,大家可以发送文字消息、表情包、红包等等,从而体现本社交模拟系统的功能。涉及的算法包括迪杰斯特拉、深度优先遍历等图论中的方法。
设计策略
整体框架如下图:
图中体现了不同类之间的关系,可以发现,我们的核心是MyNetwork类,它与所有的类之间都建立着紧密的联系。除此之外,我们还有Message、Group、Person类,分别代表信息、群组、个人,然后是十个异常类。
下面重点谈一谈我的设计策略,本单元中需要精心设计的地方有许多,如果单纯的不考虑性能的话,那问题就非常的简单了,只需要按照JML规格一步一步的照做就可以了。但是由于强测涉及到性能的约束,因而我们需要采用更加快速、效率更高的算法。首先遇到的挑战是isCircle方法,它需要我们判断两个人之间是否存在着一条路径,即是否连通,面对这个问题,我们可能会使用到DFS等遍历方法,如果找到了路径就返回True,如果没有就返回False,可是这样无疑效率是不够好的,于是受到启发,我采用了这样的思想:将所有人形成的社交网络分成许多连通分量,每一个分量(或者称为集合)中的元素都互相连通,存在路径,隶属于不同集合的两个元素之间不存在通路,这样在判断isCircle的时候,只需要判断两个人的id是否在同一个集合中就可以了。对于集合的产生,我们在addPerson时,形成一个新的分量,在addRelation时,将具有relation的两个集合合并即可。这样很好的提高了效率,不用再进行多次的遍历。
然后是getValueSum,需要我们获取组内人员两两之间的权值之和,我们同样可以采取遍历的方式来累加,但是如果有很多这样的指令到来,就可能浪费了时间上的性能,如果我们考虑事先定义一个变量valueSum,在每次有人进入群组时,就将新产生的权值累加,有人退出时,就去除这些权值,在addRelation时,加入对应的权值,这样,在getValueSum指令到来时,我们就可以直接返回valueSum了,这样的思想同样适合于求均值、求方差。
最后一个难点是求最短加权路径长度,这时使用迪杰斯特拉算法效果还不错。
测试方法
测试方法主要采取了Junit测试,对主要的方法编写测试程序,示例如下:
如果出现错误将抛出异常,从而能够知道出现错误的位置,同时也可以使用assert断言方式,来进行判断。
容器选择
在容器的选择上,我大部分采用了Arraylist,而在连通分量的地方使用的是Hashmap,在迪杰斯特拉的算法中使用的是二维数组构成邻接矩阵,这样做都有着一定的道理。在异常类的计数方面,我使用了如下的变量:
all表示总次数,ID_LIST表示id序列,TIMES为对应id触发异常的次数,现在想来其实也可以使用Hashmap或者Hashset来解决,能够进一步简化问题。
性能分析
本单元的性能要求比较高,我连续几次强测都出现了CPU超时的现象,采用优化的算法以后性能得到了一定的提升,但是仍然存在一两个测试点无法通过,我认为问题可能出现在迪杰斯特拉算法上面,我采用的是邻接矩阵,如果采用邻接表的话可能效率会更高一些,或者使用其他更好的算法。
bug修复
产生的bug主要有两种,一类是超时现象,前面已经说过了,另一类是wrong answer,主要是一些细节的地方出现了纰漏,并且在测试中没能检查出来。这意味着我的测试还不够全面,在一些自以为不会出现问题的地方没有进行严格的测试,于是导致了bug的出现。
心得体会
这一单元给我的感受,或者说是印象,主要有三个方面,第一是程序的规范性,在JML规格的影响下,我逐渐意识到了代码风格以及架构方式的重要性,在以后的编程过程中,我会更加注重代码的简洁、标准、清晰性,保证结构与内容一目了然,使得自己或是其他人能更快的明白代码的含义。第二是测试方法,以前的我基本上都是依靠课程的评测机、测试数据点来找自己程序的问题,这样固然简便,但是不能保证我的程序是完全没有问题的,只有在课下充分测试、仔细阅读代码、分析步骤结构、进行程序对拍,才能更好的完善自己的代码。第三是性能的关注,之后的设计,或者是工作岗位的要求,往往工程量会越来越庞大,这时应当考虑性能的因素,尽量优化算法,采取更为简便高效的方法来解决问题,减小开销。