BUAA OO 第三单元总结
72066004 邱小瑄
作业要求
第九次作业:根据JML规格实现Person类
和Network类
,继承异常类。
第十次作业: 新增Tag类
,Network类
也新增了queryBestAcquaintance
, queryCoupleSum
,queryShortestPath
第十一次作业:新增Message类
,Network类
再次新增 queryPopularity
, clearNoticeMessage
,deleteColdEmoji
,storeEmojiId
。
本单元的测试过程
黑箱测试
黑箱测试是一种程序测试方法,也称为功能测试。在不考程序内部结构,设计等测试其功能和行为。这种测试方法类似于将软件看作一个黑箱,只关注输入和输出之间的关系,而不管软件内部的工作原理。
课程组的评测机或是自己写的测评机都属于黑箱测试。
白箱测试
白箱测试也是一种程序测试方法,也称为结构测试。但与黑箱测试不同在于测试的时候会考虑程序功能和行为,还会深入了解其的内部结构,设计和代码的实现。白箱测试会更注重内部结构和实现细节的验证,以便提高程序的质量和稳定性。
通常在代码上手动添加System.out.println()
来观察代码的运行/状态的操作可以看作是一种白箱测试
单元测试
单元测试是针对程序中最小功能模块进行测试的方法,目的是验证该单元的功能是否按照设计要求正确地运行。由测试者通过给定输入,验证输出是否符合预期结果,同时考虑边界条件和异常情况,以确保单元在各种情况下都能正确运行。
功能测试
功能测试也就是黑箱测试,不考虑内部结构的细节测试程序的功能或特性是否符合预期。基于用户需求和功能规格,设计测试用例并执行。
集成测试
由于每一个被测试单元的正确不代表在所有模块拼接起来后依然能保留正确性,所以集成测试的目的是测试不同模块或组件之间的集成,确保它们在整体上协同工作,并验证系统的接口和交互是否正确。
压力测试
压力测试确实很压力
压力测试就是在程序完成后,利用很大且复杂的数据对程序进行在高负载和高并发测试。在这里能发现很多隐藏的bug
回归测试
回归测试是在测试代码修改前和修改后执行的测试,以确保修改后的代码仍然符合原有的功能,能大大避免新的bug。
架构设计
HW_9
本次设计主要选择的存储容器是HashMap来存储Person对象,id座位Key。
在实现MyNetwork
是添加了findRootNode
通过递归实现查找并查集的根节点,在递归返回时,将当前节点的父节点直接更新为根节点减少并查集的树的深度,提高查找操作的效率。
public Person findRootNode(Person person) {
if (node.get(person) == null) {
return person;
}
Person root = findRootNode(node.get(person));
node.put(person, root);
return root;
}
在实现queryTripleSum
方法的时候算法性能比较差,利用三重循环遍历所有 Person,判断是否存在三角关系。三重循环的时间复杂度高O(n^3),不推荐使用
public int queryTripleSum() {
int sum = 0;
for (int i : people.keySet()) {
for (int j : people.keySet()) {
if (i < j && getPerson(people.get(i).getId()).
isLinked(getPerson(people.get(j).getId()))) {
for (int k : people.keySet()) {
//continue...
}
}
}
}
return sum;
}
HW_10
本次作业我觉得最大的难点在于实现查询两个人员之间的最短路径queryShortestPath
。
public int queryShortestPath(int id1, int id2) throws PersonIdNotFoundException, PathNotFoundException {
//throws excaption...
List<Person> path = findShortestPath(id1, id2);
if (path != null && path.size() >= 2) {
return path.size() - 1;
} else {
throw new MyPathNotFoundException(id1, id2);
}
}
在此代码中调用了findShortestPath
并且使用了广度优先搜索 (BFS) 算法在图中查找两个人之间的最短路径。
while (!queue.isEmpty()) {
// 取出当前节点current
Person current = queue.poll();
if (current.equals(end)) {
// 如果current是最终节点
break;
}
if (current instanceof MyPerson) {
MyPerson myPerson = (MyPerson) current;
List<Person> acquaintance = myPerson.getAcquaintance();
for (Person neighbor : acquaintance) {
if (!visited.contains(neighbor)) {
queue.add(neighbor);
previous.put(neighbor, current);
visited.add(neighbor);
}
}
}
}
HW_11
MyNetwork类的代码怎么可能少与500行
本次作业添加蛮多需求的,基本上都是按照jml规格来写,没有太多复杂算法的实现。常用的存储容器还是ArrayList 和 HashMap,除了deleteColdEmoji
这个方法的实现我选择的是 Iterator,因为这样更能安全地遍历与删除元素。
JUnit测试
JUnit测试是一种白箱测试,在作业中需要验证前置条件、运行测试方法、验证后置条件等步骤,我常采用的方法是assertEquals
,确保Junit的测试数据输出与规格输出一致。
心得体会
在本单元中,第一次接触JML规格和JUnit测试这一概念,确实相对于前两个单元会比较容易,只需要按照其规格编写代码,有些看是很长的规格其实只要return就完事了,但也有一大长串的jml规格需要花很长的时间来理解和思考用什么容器,什么算法能较好地实现。在这个单元中,意识到在图这方面的算法比较差,需要花比较长的时间去实现有关算法。每一个单元的作业还是挺有挑战性的,但同时学到的东西也很多,理解JML规格对一个程序员的重要性。