BUAA_OO第三单元总结
一. 实现规格采取的策略
- 这三次作业我主要采用的就是单个类,单个方法逐个攻破的方法。首先通过JML规格在每个类前定义的变量名称定义好必要的变量。然后在逐个单独实现不同的方法,由于每个函数单独规格都不是很复杂,所以实现并不是很困难。而在实现的过程中,如果需要定义额外的变量或方法,那么就再回过头定义即可。
- 在完成了各个方法的过程中,大致也对每个方法有了了解。在完成了所有的方法之后,从整体上理解每个类的在整个社交网络中的含义,每个方法的含义的具体意义等。接下来从其具体含义出发检查每个方法的功能是否得到满足。
- 最后从头检查,检查方法的实现是否严格满足了规格。
二. JML规格测试方法和策略
-
JUnit注解+断言检测
利用JUnit单元测试可以实现比较及时的对所写部分函数的有效性正确性检测,具体的细节在这里就不多提,最主要的就是掌握相关的注释含义和一些实用的断言检测,下面列举一些断言检测:
| assertArrayEquals(expecteds, actuals) | 查看两个数组是否相等 |
|---|---|
| assertEquals(expected, actual) | 查看两个对象是否相等 |
| assertNotEquals(first, second) | 查看两个对象是否不相等 |
| assertNull(object) | 查看对象是否为空 |
| assertNotNull(object) | 查看对象是否不为空 |
| assertSame(expected, actual) | 查看两个对象的引用是否相等 |
| assertNotSame(expected, actual) | 查看两个对象的引用是否不相等 |
| assertTrue(condition) | 查看运行结果是否为true |
| assertFalse(condition) | 查看运行结果是否为true |
| assertThat(actual, matcher) | 查看实际值是否满足指定的条件 |
-
自己搭建自动测试机来进行正交检测
利用JUnit测试感觉并不完备,在自己的代码都完成的情况下,可以根据题目内容设计相关自动评测机,利用Python即可简单搭建。而正交检测就是对于某个实现特定功能的函数,采用两种或多种完全不同的实现方法,然后将两个不同实现的程序共享一组测试数据,并在评测机上对比输出是否相同。这种方法适用于周围同伙一起对拍的情况。在这次测试中,比如为了实现比较高的性能,我们会采用诸如并查集等较为高级的方法,而这些方法我们可能并不熟悉,所以有较大的出错可能。而这个时候我们还可以用常规方法再写一次,这次的实现出错的概率较小,而如果两个程序的经过充分的测试,得到的结果都相同,那么便可以认为待检测代码是正确的。而如果出现了问题,那么可以进一步查找究竟是哪一个代码写错了,修改完之后重复上述测试方法。
-
自己和他人用测试机进行对拍检测
采用正交测试需要花费比较大的工作量来写两份重点部分实现思路不同的代码,而在这次作业中,我们完全可以让不同的人将代码聚在一起,共用一组测试数据进行输出对拍,如果出现错误,那么我们就可以先分析错误情况,如果其他人答案都相同,而只有少数的不同,那么极有可能就是少数代码有误。当然,具体的情况还要在进行了分析之后才能确定。这种方法原理和第二种相同。
三. 容器选择和使用经验
在这一单元作业中,我并没有在容器上花太多的时间研究,我就仅仅用了ArrayList来实现相关的功能。而这次让我有所收获的是第一次将迭代器(iterator)用在了相关容器的查找和删除操作上。ArrayList以及其他大多数容器都统一支持利用迭代器来进行查找增删操作。ArrayList的添加操作可以直接用ArrayList.add(int index, object element)来实现,但是却没有提供ArrayList.remove(int index)等方法,需要iterator.hesNext(), iterator.next()以及iterator.remove()来实现查找和删除。
四. 性能问题
第一次作业
第一次作业强测并没有出现超时现象,我的方法就是采用了并查集来非常快速的实现求连通分量和判断二人是否在同一连通分量上。其余并没有做什么特别的优化。
第二次作业
第二次作业强测也没有出现超时现象。我采取的方法就是首先在连通分量上还是采用了并查集的方法,同时在MyGroup类中的getAgeMean和getAgeVar上做了小优化。我注意到这两个函数如果没有向组中插入或删除人的操作的话,每次调用这两个函数所得到的结果是相同的。所以我设置了mmAge和vvAge两个布尔类型变量,当距上一次调用同一个函数中间没有人的增删操作时,保持false,并直接返回记录上次该函数返回值的ageMean或ageVar函数。而如果增删了人,那么mmAge和vvAge均变为true,再调用getAgeMean和getAgeVar时按照规格重新计算返回值,并赋予到ageMean或ageVar上,并将对应的mmAge或vvAge赋值为false。这种优化就能省去很多重复计算年龄的平均值和方差的工作,只在必要的时候计算。
第三次作业
第三次作业我并没有在第二次作业的基础上有其他优化的方案,其中在寻找最短路径的函数中我用的是最为基本的图的深度遍历,效率应该不算高。
五. 作业架构设计
第一次作业
图模型构建:本次作业我采用了并查集的方法来对每个人进行存储记录。我为了方便,采用了开辟一个5000个长度的int类型数组parent,用以记录每个点所属连通分量中的祖先,又开辟了一个5000个长度的int类型数组size,用以记录每个节点所处连通分量的点的个数(用于细节优化)。
维护策略:本次实现较为简单,故采取的是逐个函数攻破的方法,也就是简单的按照每个函数的JML语言的说明来实现相关的函数。
第二次作业
图模型构建:本次作业对于人的节点管理主要仍然采用了并查集的方法,管理方法和第一次作业基本相同。
维护策略:仍然采用了上次的作业思路,这次作业和上次比最明显的改变就是增加了删除的操作,而JML中涉及到的删除操作均在Network类中,但是我在写删除操作的时候采取的原则是涉及到删除哪个类中的Person,就在哪个类中增添删除的函数,而不是在Network中定义许多不同的删除操作函数,这样首先并不符合面向对象的思想,而且并不方便改bug维护。
第三次作业
图模型构建:本次作业我除了仍采用并查集来存储图,为了实现寻找最短路径的方法,我还单独用了邻接矩阵的方法存储了人之间的联系。
维护策略:本次维护策略和第二次作业的方法一样,每当需要的操作JML规格中并没有给出时,都会在适当的类中定义相关方法以实现功能。
浙公网安备 33010602011771号