面向对象第三单元作业总结

张昊博-22371559 2024-05-16 13:42:37

第二单元:JML规格化设计社交网络 - 面向对象设计与构造

前言

第三单元聚焦于JML规格,在算法方面的要求较前两个单元有所下降,但规格与实现相分离的软件设计原则也导致了一定的区分度,也给了我们不少发挥的空间。

任务概览

  • hw9: 建立一张无向带权图,支持查询连通块、三元环个数。
  • hw10: 支持Tag,支持最短路查询。
  • hw11: 添加了消息发送与接收等。

此外,每次作业都需要对特定方法编写一个Junit测试方法。

代码架构分析

本单元的架构均来自于课程组已经给定的JML,仅在实现上有些许不同。

本单元的问题实际上是图论问题。Network作为一张大图,Person类就是图的顶点,而Acquaintance类似于边。

通过阅读JML规格的描述,我采用了临界矩阵来存储图,为了优化性能大量使用HashMap作为容器。对于一些特定数据,如每个person的messages需要有头部插入的功能,我选择了链表以达到O(1)。以上是数据结构方面的优化,算法方面的优化如下:

  • bfs实现最短路查找

最短路算法的实现有不少,我们注意到本次作业中的边权始终为1,故采用bfs以O(n)的时间复杂度解决即可

public int bfsPath(Person from, Person to, ArrayList<Person> bfsTrack) {
        ArrayList<Person> bfsQueue = new ArrayList<>();
        ((MyPerson) from).setFlag(true);
        bfsQueue.add(from);
        while (!bfsQueue.isEmpty()) {
            MyPerson newFrom = (MyPerson) bfsQueue.get(0);
            int dis = newFrom.getDis();
            for (Person person : newFrom.getAcquaintance()) {
                if (person.equals(to)) {
                    return dis + 1;
                }
                MyPerson myPerson = (MyPerson) person;
                if (!myPerson.getFlag()) {
                    myPerson.setFlag(true);
                    myPerson.setDis(dis + 1);
                    bfsQueue.add(person);
                }
            }
            bfsTrack.add(bfsQueue.get(0));
            bfsQueue.remove(0);
        }
        return 999999;
    }
  • 连通块的查询与维护

我建立了Block这个类来作为连通块,主要的复杂度集中在删边时重新确定连通块上,代码如下:

public void split(Person person1, Person person2) {
        int homeBlock = person2Block.get(person1);
        MyPerson from = (MyPerson) person1;
        MyPerson to = (MyPerson) person2;
        for (Person person : block2Persons.get(homeBlock)) {
            ((MyPerson) person).setFlag(false);
        }
        ArrayList<Person> bfsTrack = new ArrayList<>();
        if (!bfs(from, to, bfsTrack)) {
            blockId++;
            blockNum++;
            block2Persons.put(blockId, new HashSet<>());
            for (Person person : bfsTrack) {
                block2Persons.get(homeBlock).remove(person);
                block2Persons.get(blockId).add(person);
                person2Block.replace(person, blockId);
            }
        }
    }
  • 数据的动态维护

JML规格中对于查询方法的复杂度达到了O(n),O(n^2)乃至O(n^3),为了不超时,将查询复杂度分摊到每一次增删上以保证最坏时间复杂度,是一个很自然的想法。相关的属性有ValueSum TripleSum Iscircle等。

规格与实现分离

契约式设计是一种软件设计方法,它将程序中的规格(契约)与实现(代码)分离开来。在契约式程序设计中,规格是描述组件、函数或类的预期行为的明确约定,而实现是代码的具体实现细节。

这个原则实际上我们经常接触:做编程题时题目只给出了要求完成的功能,没有限制实现的方法。我们可以认为这就是一种规格与实现相分离。

软件测试

软件测试是指在软件开发过程中使用各种技术和工具对软件系统进行评估和验证的过程。它的目标是发现潜在的错误、缺陷或问题,并确保软件系统符合预期的质量标准和用户需求。

黑白箱测试

黑箱测试和白箱测试是软件测试中两种不同的方法。

黑箱测试(Black Box Testing)是一种测试方法,它关注于测试系统的功能和行为,而不考虑系统的内部结构和实现细节。测试人员在黑箱测试中没有访问代码、设计文档或内部知识,他们只关注于输入和输出之间的关系,以验证系统是否按照预期运行。黑箱测试侧重于检查软件是否符合规格说明、用户需求和预期功能。

白箱测试(White Box Testing),也被称为透明盒测试或结构测试,是一种测试方法,它关注于测试系统的内部结构、设计和实现细节。测试人员在白箱测试中可以访问代码、设计文档和内部知识,他们使用这些信息来设计和执行测试用例,以验证系统的内部逻辑是否正确。白箱测试侧重于覆盖代码路径、检查边界条件和错误处理机制,以确保软件在技术层面上是正确的。

测试阶段

  • 单元测试(Unit Testing):单元测试是对软件中最小的可测试单元(如函数、方法或类)进行测试的过程。它的目标是验证这些单元的行为是否符合预期,并尽早发现和修复可能的问题。
  • 功能测试(Functional Testing):功能测试是一种测试方法,用于验证软件系统的功能是否按照规格说明和用户需求正常工作。它关注于系统的功能和行为,而不考虑内部实现细节。
  • 集成测试(Integration Testing):集成测试是在多个组件或模块结合在一起形成系统之后进行的测试过程。它的目标是验证这些组件之间的接口和交互是否正常,并确保它们在集成后能够正确地工作。
  • 压力测试(Stress Testing):压力测试是一种测试方法,用于评估系统在负载增加和极限条件下的性能表现。它通过模拟大量并发用户或大数据量的负载来测试系统的稳定性、吞吐量和响应时间。
  • 回归测试(Regression Testing):回归测试是在对软件进行更改或修复后重新执行旧的测试用例,以确保新的更改没有引入新的错误或导致现有功能发生问题。它的目标是验证软件在进行修改后仍然保持正常的功能和行为。

数据构造策略

数据构造是软件测试中一个重要的任务,它涉及生成测试数据以满足测试需求和覆盖测试目标。以下是一些常见的数据构造策略:

  1. 正常值测试:使用符合预期范围和规格要求的正常数据进行测试。这些数据可以代表典型的使用情况,以验证系统在正常条件下的功能和性能。
  2. 边界值测试:在测试数据中选择接近或恰好位于边界值的数据,包括最小值、最大值和临界值。边界值测试可以揭示在边界处可能出现的问题和错误。一个典型的例子是hw10强测中的hw10_strong_1111.txt.
  3. 异常值测试:使用非预期或无效的数据进行测试,包括错误的格式、无效的输入和异常情况。这可以帮助发现系统对异常情况的处理能力和错误处理机制。异常值测试对应本单元作业中的一系列异常类。
  4. 随机化测试:使用随机生成的数据进行测试,以模拟真实世界中的各种情况和场景。随机化测试可以帮助发现常见的错误和异常情况。
  5. 组合测试:将不同的输入参数组合在一起进行测试,以测试系统对不同参数组合的相互作用和影响。这可以帮助发现参数之间的依赖关系和潜在的冲突。

Junit测试

本单元每次作业都针对了一个特定的函数编写JUnit测试。

需要对所有JML规格来编写测试代码,不能遗漏任何一个分支或变化或者不变。

由于直接面向JML来编写代码,JUnit测试可以有效保证方法满足契约的规范,也就是说经过检验的方法的正确性得到了保障。

Debug & Hack相关

前两次作业都均强测满分且未被Hack,可惜的是在第三次作业没有判断Tag的size导致出现了除0错误,也是我没有认真阅读JML并进行对照导致的。此外,由于互测数据规模限制,仅在第一次作业中采用大规模数据与多条查询指令把别人T了。

心得体会

在这个单元中,我学习了JML规格的理解与实现,并应用它们来完成社交关系模拟系统的开发任务。这个单元的学习使我对JML规格的重要性和应用有了更深入的认识,并且提升了我的代码质量意识和能力。

通过学习JML规格,我意识到规格在软件开发中的关键作用。规格定义了组件的预期行为、约束条件和接口要求,能够明确地描述出组件应该如何正确使用和与其他组件交互。这为开发过程提供了清晰的指导,减少了开发人员对实现细节的依赖,提高了代码的可维护性和可扩展性。

...全文
43 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧