其他分享
首页 > 其他分享> > OO第三单元回顾总结

OO第三单元回顾总结

作者:互联网

目录

前言

本单元围绕JML进行规格化设计,契约式编程的思想基本上贯穿了整个单元。

本单元最大的新体验在于:作为大工程中某一部分代码的实现者,履行应尽的责任,而非像前几个单元一样自行设计完整架构并完成所有代码。更直观的感受在于:由于课程组提供了细化到每一个类和类方法的JML描述,完成作业更像是做一道道小题,颇有一种“闯关”的快感,写起代码来体验极佳!

尽管如此,本单元依然存在一些难点,也存在许多值得探讨的课题,接下来将详细描述。

一、架构设计

本单元主要需要设计Network中的GruopPerson实例的生成、关系建立和查询等方法。其中涉及一些图论知识。

1. 第一次作业

第一次作业的难点在于完成query_circlequery_block_sum两种查询。在实现中,本人使用了并查集算法。

具体而言:结点的连通关系是一种等价关系,具有自反性、对称性和传递性,图中的所有结点可以被分成多个连通分量(参考离散数学知识)。而本单元Person实例间的连通关系是同样的道理,因此可以根据是否连通,将Person划入到多个集合中,互相连通的Person在同一个集合。

在代码实现上,集合的概念通过并查集算法实现,位于同一个集合的Person实例形成一个树状结构,则在查询时,可:

2. 第二次作业

第二次作业的难点在于完成query_least_connection查询,需要设计最小生成树的算法。在实现中,本人使用了Kruskal算法

具体而言:Kruskal算法不断从未选中的边集中选择权重最小的边,尝试将此边加入已选中的边集,并判断引入此边是否会导致的出现。实现的难点就在于判断是否出现了环。在本人的具体实现中,此处也使用了并查集算法,当两个结点依靠已选中的边可连通时,两个结点位于同一个集合。由此,只有当前边连接的两个结点不在同一个集合时,此边才能成功加入。以下图为例:

假如当前结点连接如左图所示,且Kruskal将继续尝试将Edge1和Edge2加入边集:在判断Edge1时:由于结点2和3已经位于同一个集合,因此无法继续加入;而在判断Edge2时:结点5和7位于两个不同的集合,因此可加入此边。

3. 第三次作业

第三次作业的难点在于完成send_indirect_message查询,需要完成最短路径算法。在实现中,本人使用了堆优化的Dijkstra算法

具体而言:Dijkstra算法从当前未到达的结点集合中,选取最近的结点标记为到达,并用此节点更新其他未到达结点的距离。由于需要不断更新结点的当前距离,并涉及选取最近的结点,因此类似于优先队列问题,可以使用小顶堆进行优化。在本人的具体实现中,使用了Java自带的PriorityQueue容器完成。

二、测试数据准备

本单元需要利用JML规格来准备测试数据。总结而言,满足各个类方法前置条件的测试数据均应该覆盖。而本单元真正具有前置条件的类方法非常少,大部分都通过抛出异常解决,因此,测试数据就几乎涵盖了所有可能的情况。

具体来说,本人在构造测试数据时,主要分别构造正常数据和异常数据,并分别对每个异常都构造数据。以addToGruop方法为例:

addToGroup方法设计到三种异常。因此,在构造测试数据时,需要包含:

  1. 正常的测试数据:存在idid1Person,存在idid2Group,且Person目前不在Group
  2. 涉及GroupIdNotFoundException的测试数据:不存在idid2Group
  3. 涉及PersonIdNotFoundException的测试数据:不存在idid1Person
  4. 涉及EqualPersonIdException的测试数据:idid1Person已经位于idid2Group

三、性能分析

本单元严格限制了代码运行时间,以下总结了可能出现性能问题的指令,与本人的解决方案。

1. query_circle与query_block_sum

除了上文中提到的,这两条指令使用并查集完成外。在实现过程中,还需要进行路径压缩。具体而言,路径压缩通过重构并查集的树状结构,减少查询根结点时间。以下图为例:

在查询到结点3的根结点为结点1之后,使结点3直接指向结点1,由此可以减少下次查找结点3根结点时花费的时间。

2. query_group_value_sum

如果在每次收到qgvs指令后,都通过二重循环,遍历Group中两两结点的关系,则时间复杂度为O(n^2),无法通过本单元测试。解决方案是,在Group中维护一个变量valueSum保存此查询的答案。valueSum需要在遇到以下指令时检查是否需要更新:

  1. add_relation:需要检查两个Person是否都位于同一个Group,如果是,则需要更新valueSum
  2. add_to_group:需要检查加入的Person与目前位于Group中的每一个Person是否有关系,如果是,则需要更新valueSum
  3. del_from_group:需要检查删除的Person与目前位于Group中的其他每一个Person是否有关系,如果是,则需要更新ValueSum
3. query_least_connection

如上文所述,利用依赖并查集的Kruskal算法,可以满足时间限制。

4. send_indirect_message

如上文所述,利用堆优化的Dijkstra算法,可以满足时间限制。

四、Network拓展

本人思路如下:构建AdvertiserProducerCustomer三个接口,这三个接口均继承Person接口。并构建Product类,封装产品信息。

Advertiser接口见下。Advertise维护productInfo数组,通过addAdvertise()方法接受来自Producer的产品信息,通过getAdvertise()方法提供产品信息。

Producer接口见下。Producer拥有一个Product实例,封装自身产品信息,同时Producer拥有一个Message变量,存放买家信息。Producer通过useAdvertise()方法向Advertiser提供自身产品信息,通过receiveBuyInfo()方法获取买家信息。

Customer接口见下。Customer通过buy()方法向Advertiser表明购买意愿。由于buy()方法设计消费者的具体偏好,因此此处并未给出具体的JML规格。

五、学习体会

总结而言,本单元学习了契约式编程的重要思想,并通过三次单元作业,掌握了读写JML规格语言的能力,能根据JML规格语言实现代码。就笔者而言,这是头一次涉及到多人合作完成代码的方法和思想,有非常独特的体验。

在完成单元作业方面,本单元确实难度较低,学习过程非常轻松愉快。同时,本单元间接帮助笔者回忆了离散数学和数据结构的一些相关知识。也只有在这种情况下,笔者才能意识到自己对于过去学过的知识遗忘之快,可见过去学习并不那么扎实。

最后,希望能顺利完成第四单元,继续加油!

标签:OO,结点,Group,测试数据,Person,回顾总结,query,单元
来源: https://www.cnblogs.com/syjzdkfc/p/16344802.html