OO第三单元总结

概述

  第三单元作业,我们基于给定的JML规格,实现了一个社交网络的架构,本质上是无向加权连通图的构建,以人为节点,关系为连线,支持消息的直接、间接发送。同时,为了方便社交网络的处理,我们还引入了群这一概念,将指定的人加入群中,方便节点的批量处理如群发消息等。此外,我们还引入了异常处理系统,对于图的处理中出现的问题,抛出相应的异常,方便找出错误的同时能够保证程序的继续执行。

设计策略

    由于类的规定,方法的作用均以JML的形式予以规定,我们需要做的只是实现课程组让我们做的事情,因此设计策略由课程组而定。

  在实现JML规格时,我主要先通过方法的名称来大致判断这个方法的具体作用与效果,然后通过阅读JML规格来构思实现方法,并检查能否严格满足JML的定义,最后将自己的构思付诸实践。在实现顺序上,大致按照先易后难的原则,建立起节点、关系的基本结构,再实现查询、计算等的功能,最后再实现异常的处理。

测试策略

  由于方法已经定义好,因此架构方面应当不会出现问题。在前两次测试中,我的测试主要针对方法的正确实现、异常的抛出,主要通过手动构建样例的方式。但在第二次测试中,我在查询连通块的个数方法中出现了TLE的问题。因此,我在第三次作业的测试中,还通过程序实现大规模数据的测试,即对某一(几)条指令进行循环输出,用于构造大规模的样例,以检查程序的性能,并针对此进行了适当的优化(虽然最后依然未能通过强测 = =)。

容器的选择

  由于所有人id是唯一的,因此很自然的想到对于任何以人为个体的容器,均采用HashMap为容器,以id为key值进行映射,从而方便地进行查找、插入、删除等操作,复杂度理想情况下均为O(1),但注意对于某些特定顺序的容器,HashMap并不适用,如方法getReceivedMessage中,要求取出最近加入的几个元素,此时Hashmap难以实现方法要求,因此我使用了ArrayList,通过头插的方式实现了元素的时间排序,可以方便、简单地实现此功能。

性能优化 

  本单元中测仅包含少量正确性的测试,但我们有必要注重性能的优化(说的就是我自己= =)。值得注意的是,关于JML规格的定义,只要严格满足其要求、实现后的结果即可,中间实现的过程不一定按照JML的描述,否则很容易出现性能的问题(比如我),在此对几个容易出现的性能问题进行分享。

  • 对于连通块个数的计算,JML规格要求的是遍历所有人(节点),一旦找出和遍历过的每个节点都不相通的点,便视为找到一个新的连通块,这样实现,正确性无可非议,但如果严格按照JML规格实现,将需要两层循环调用isCIrcled方法,即两层循环进行图的遍历,时间复杂度令人发指,因此TLE合情合理,我们应当找到一个优kstra化的方法。为此,我采取了图的深度优先遍历方法,复制一份结点容器,从该容器中取出一个点,对这个点进行深度优先遍历,将遍历到的点从容器中去除;每次遍历导致连通块数量加一,循环直到容器为空即可。如此,我们仅需要进行n次图遍历即可(n为连通块个数)。
  • JML中方法的调用也不一定要照抄,如在计算年龄方差时,循环调用求年龄平均方法导致大量冗余计算并最终导致TLE,事实上设置一常量保存结果即可。
  • 在寻找最短加权路径时,一般采用dijkstra算法,但遍历每个点并更新其所通节点的路径最小值的复杂度达到O(n2),在节点较多时,这样的算法速率是不能接受的,为此我们采用堆作为容器来进行优化,从而在寻找距离最短边时,复杂度可由O(n)优化到O(logn).
  • 为了避免大量重复的查询操作浪费效率,我对某些查询采取了更新式的查询方法,如连通块的个数会在加入新节点时自动增加,仅在加入新边时会调用查询方法重新计算。

心得体会

  由于JML规格的存在,此次作业中我不必关心架构的设计,而只需关心方法的实现(在一堆TLE之后更认识到了算法优化的重要性),因此架构设计不必赘述,按照图的结构和JML规格按部就班地完成即可。我认为JML规格就像一把标尺,只要该方法经JML衡量合格,无论内容如何,均可满足需求。因此JML在用于描述设计需求,避免设计人员意图的模糊性等方面是高效率的。同时,在满足JML规格时,我们应当跳出JML的描述语言,思考如何在正确的同时高效地、简洁地满足设计需求。

posted @ 2021-06-01 19:41  张什么来着  阅读(57)  评论(0)    收藏  举报