Loading

OO Unit3 Summary

OO Unit3 Summary

前言

这单元真是暗流涌动。

​ ——沃兹基-硕德

你们已经学过很多东西了,所以你们要留心去思考一些问题,才能把自己学过的东西融会贯通起来。

​ ——王旭老师

正文

实现规格的设计策略

我实现规格的策略是:

  • 先通读一遍规格,尝试通过规格与合理猜测将每个接口和自己学过的模型对应,对应好之后就可以对着规格写代码实现。
  • 遇到需要优化性能的时候,尽可能保证优化后的代码和JML看起来映射关系比较清晰。

我个人认为自己的这种策略的主要缺点在于容易陷入已有的思维定势中,从而可能导致规格理解错误。

读规格的过程中,遇到的困难主要是规格太长,读了十几行就会忘掉,所以要读好几遍才知道这个方法到底是干啥的。另外还有个隐含的困难是,如果这个软件的功能自己之前没接触过,不清楚咋建模的话,那么读规格可能和读代码一样难以还原出整体的设计。这单元作业的图模型及其操作大家可能比较熟悉,所以没出现很明显的第二个问题。

基于规格的测试策略

关于测试方法和策略,由于有已经保证了正确性的规格,所以我的测试思路是这样的:

  • 首先重新仔细比对规格与自己的代码实现,看自己有没有看错规格,有没有漏掉什么东西。通过这一步,我一般可以找到漏实现以及误实现的一些问题。对于没有提供规格的异常类,这一步就是仔细阅读指导书,看看是不是漏读或者错读了一些要求(比起读JML,读指导书这种自然语言时要更加仔细)。
  • 上一步完成之后,找别的同学pvp对战,分别用自己的对拍器以及别人的写的对拍器进行对拍,来看看不同同学对规格的理解和实现是否存在偏差。我的对拍器最开始只是随机数据对拍,后来改进成了伪单元测试类型的数据生成,即可以先生成几十组专门测某个方法的数据,全部AC之后再生成几十组专门测另一个方法的数据,这样下去,实现“单元测试”的效果。
  • 功能上感觉没什么问题之后,手捏一些极端数据,测一测复杂度高的方法在运行时表现如何,排除一些CTLE的风险。

简单来说就是:重读规格,对拍,测极限数据。

值得一提的是,虽然老师这个单元非常强调要做单元测试,并且我在第一单元就有了使用junit进行单元测试的经验,本单元理应会用它,但是我尝试了一次之后发现在这单元使用有些多余和鸡肋,原因是我发现在存在规格的情况下,设计单元测试用例的时候需要阅读规格重新理解需求,如果规格理解错了,那么单元测试的时候设计出来的用例肯定也错了,这种原因导致的bug很可能只有和别人交流规格的理解或者暴力对拍的时候才能发现;如果重读规格的时候读对了,那么顺手就能看出来代码实现的对不对(可能还是项目的规模比较小吧)。所以我认为对于这单元的作业来说,只要仔细理解规格并且进行对照就可以完全起到单元测试的效果。但是以后开发更大的项目并且没有JML这种相对严谨的规格描述的时候,那么高覆盖率的单元测试会是软件质量的坚实保证。

在互测中,我也是使用了评测机测试 + 极限数据测试的方法,在第一次作业中hack成功,在后两次互测中没有什么收获。

容器选择和使用的经验教训

关于容器的选择,首先我们要知道每个容器主要是基于我们学过的哪种数据结构来实现的,然后阅读规格,观察我们需要对数据做什么样的操作,最后再决定到底使用什么样的容器。下面举几个例子:

  • Person中的queryValue方法,需要快速查找某个id的人是否和当前人是连接的,如果直接连接则返回边权,这种随机访问且键值对中键不适合用数组下标表示的时候就比较适合用HashMap去实现快速查找。所以在存储邻接表的时候,每个头结点对应的“邻接链表”完全可以使用HashMap去实现,不一定局限于使用链表。
  • PersonMessage的存储,可以发现getReceivedMessage对其有顺序的要求,以及Network中发送信息的方法要求将新的信息头插到PersonMessage中,且我们从不需要遍历这部分信息,所以可以考虑使用LinkedList存储Message
  • 同样是MessageNetworkMessage则要求我们快速增删和查询,所以这里用链表就比较低效了,可以换做Map来实现。

在使用容器的时候,建议对某个操作不熟悉时仔细阅读文档并查看实用的避坑教程,从而避免对容器的操作产生误解。说实话这单元最让我感觉没底的地方就是容器的操作,我生怕容器会做一些我没有注意到的事情导致结果错误,而这种错误通过对照JML是根本看不出来的(容器的操作都是抽象的,水很深,我怕自己把握不住呀)。我自己在本单元很幸运没遇到相关问题,但是在第9次作业中,某位同学向我反映遇到了一个容器使用相关的问题是ArrayList的remove方法调用问题ArrayList包含两个remove方法:

public E remove(int index);  
public boolean remove(Object o);

如果是ArrayList<Integer>的话,调用remove方法的时候,假如是remove(id)这种传入int类型的id,调用的是第一个方法,如果这个时候我们期望删除的是元素1,那么将执行错误的方法。这个时候就需要使用包装器包装之后再调用方法。连最熟悉的ArrayList都能摆我们一道,可见在使用别人的东西的时候要有多小心。

性能问题的避免

关于性能问题,笔者在本单元并没有因为性能问题而失分。之所以能避免这些问题,原因有以下几点:

  • 笔者课余时间会写点算法题,对基础的数据结构以及优化技巧比较熟悉,比如本单元作业中就使用了如下优化技巧:
    • 通过缓存的方式维护均值,简单推柿子快速算方差等信息。
    • 数据结构优化dijkstra算法。
    • 并查集维护具有传递性的信息。
  • 在管理数据时选择了合适的容器。
  • 本地自测了几组可能卡超时的数据,比如第一次作业中卡暴力qbs的数据,第二次作业中卡没使用缓存的qgav,第三次卡裸的dijkstra
  • 官方测试数据在一定程度上放了水,卡了性能,但没完全卡。最典型的就是查询name_rank指令不超过333条,所以不需要使用平衡树等数据结构动态维护。

架构梳理以及图模型的构建与维护

架构:这一单元我自己没有进行架构的设计,架构基本已经被官方接口定好了,下面只展示一下第三次作业中官方接口的架构是什么样的:

我只是把官方接口都简单地实现了一遍,用接口类型引用自己实现的类的实例而已。

建图的话通过阅读规格,最终确定使用的存储结构是动态邻接表,单个的邻接链表使用的不是链表而是HashMap实现的,头结点列表使用的也是HashMap实现的。

维护图就是通过理解规格,使用相应的图的基本操作(增删改查)来维护图中的信息,在有必要的时候使用数据结构(现有容器或者自己手搓)或者缓存等技巧加速维护。

虽然已经通过接口把架构几乎定死了,但是在具体实现的时候,我们可以采用任意合理的实现方法(比如想要优化的话一般需要在规格的基础上多维护一些数据),并且在需求变复杂的时候可能需要进一步建立层次关系(比如表情信息可能还会分为默认表情和自定义表情,红包还分为拼手气和普通红包)。可惜本单元的作业没有像前两个单元的作业那样逼迫我们不得不去进一步建立层次关系(这个痛点,它不够痛),即使需要建立,可能到时候官方给的规格中也会有所体现(比如增加接口继承),不太需要自己去想。

感想

如果能有比较完备的证明设计的正确性的工具(吴佬gkd),并且有通过规格自动生成代码的工具,那么以后人们只需要把精力集中在设计上就可以了。就算只能做到第一点,那么写代码也只是个体力活和时间问题。可是实际开发的时候JML的编写成本可能比较高(实名感谢课程组花这么大心思编写JML),验证起来也有一定的困难,并且,实际开发的产品投入应用的时候性能势必是一个很重要的问题(想想选课网站),这就导致规格和代码实现有时候不能很好地对应,可能会导致bug的产生(这也是这个单元最刺激的地方了)。

要是工作时能对着JML写代码就好了。

单元测试和JML还是应该早些广泛宣传。早介绍的好处个人认为有以下几个:

  • 本单元我在层次化设计这个方面其实收获不是很大,但在设计方面的主要收获是学会了一种严谨的表达设计的方式。通过简单接触JML,我对于如何表达设计以及准确表达自己的设计的重要性有了更深入的理解。我在第一单元时,有一个很大的问题就是不知道如何去表达自己的设计,这导致我经常写着写着发现不太对劲然后部分重构,后来使用了类似于写伪代码的方式去描述设计的类(现在看来算是一种不严谨的规格吧),才逐渐地可以在真正写代码之前发现一些问题,但自然语言和伪代码对一个人来说还是会有二义性的,所以当时虽然少了大重构,但是局部小重构还是不少的。现在来看,如果当初知道并使用这种准确的描述设计的方式,可能第一单元的体验会更好一点。
  • 提前向同学们安利单元测试(其实第一单元研讨课上已经有同学介绍了),能够更全面的测试第一单元的复杂程序。我个人在第一单元的时候使用了单元测试,这帮助我比较全面的测试了读入解析部分,de出了好多递归下降的bug,使得我在第一单元没有因为功能错误损失分数。如果最开始理解不了如何设计,先学会如何测试也很好呀。

本单元的作业主要还是考察是否细心,一方面是细心读规格,另一方面是细心做测试,二者只要能做好一个,那么应该就不会翻大车。但是从学习知识和技术的角度而言,我们最好还是要学会咋写规格,以及多读一些写得比较好的测试代码,学习如何写更优秀的测试。更重要的是,学会把自己学过的东西和实际问题联系起来,多思考,切忌纸上谈兵,切忌以完成作业为目标而得过且过,OO课的东西绝对不只是完成作业时体会的那些。

posted @ 2021-05-30 12:29  BUAA-Wander  阅读(299)  评论(0编辑  收藏  举报