OO第9-11次作业总结

JML单元总结博客

1、设计分析

1.1 第九次作业--实现路径类和路径容器类

摸鱼划水?!就是一个简单的增删查问题。不过注意,涉及统计的方法,需要做均摊。因为,增删方法调用较少,把计算任务分配到这些方法中,即可减少平均时间复杂度。

巧用多重容器,会带来意想不到的效果。另外,善用Hash容器,因为它们平均增删查时间复杂度均为O(1)。

UML类图

image

复杂度分析

方法

Method ev(G) iv(G) v(G)
mypath.MyPath.compareTo(Path) 6.0 4.0 7.0
mypath.MyPath.containsNode(int) 1.0 1.0 1.0
mypath.MyPath.equals(Object) 5.0 3.0 5.0
mypath.MyPath.getDistinctNodeCount() 1.0 1.0 1.0
mypath.MyPath.getNode(int) 1.0 1.0 1.0
mypath.MyPath.getNodes() 1.0 1.0 1.0
mypath.MyPath.hashCode() 1.0 1.0 1.0
mypath.MyPath.isValid() 1.0 1.0 1.0
mypath.MyPath.iterator() 1.0 1.0 1.0
mypath.MyPath.MyPath(int...) 1.0 2.0 2.0
mypath.MyPath.size() 1.0 1.0 1.0
mypath.MyPathContainer.addPath(Path) 3.0 5.0 6.0
mypath.MyPathContainer.containsPath(Path) 2.0 1.0 2.0
mypath.MyPathContainer.containsPathId(int) 1.0 1.0 1.0
mypath.MyPathContainer.getDistinctNodeCount() 1.0 1.0 1.0
mypath.MyPathContainer.getPathById(int) 2.0 1.0 2.0
mypath.MyPathContainer.getPathId(Path) 2.0 3.0 4.0
mypath.MyPathContainer.MyPathContainer() 1.0 1.0 1.0
mypath.MyPathContainer.removePath(Path) 2.0 5.0 6.0
mypath.MyPathContainer.removePathById(int) 2.0 2.0 3.0
mypath.MyPathContainer.size() 1.0 1.0 1.0
Total 37.0 38.0 49.0
Average 1.76 1.81 2.33

Class OCavg WMC
mypath.MyPath 2.0 22.0
mypath.MyPathContainer 2.1 21.0
Total 43.0
Average 2.05 21.5

耦合度分析

Class Cyclic Dcy Dcy* Dpt Dpt*
mypath.MyPath 0.0 2.0 2.0 2.0 3.0
mypath.MyPathContainer 0.0 5.0 9.0 2.0 2.0
Total
Average 0.0 3.5 5.5 2.0 2.5

复杂度、耦合度都不高,毕竟是容器类,各实现各的。

1.2 第十次作业--在路径容器的基础上,实现图算法(连通性和最短路径)

这次还是相对简单。因为,我们的图是无权图,所以实现最短路径只需要用BFS,当第一次搜索到一个点的时候就表明它已经被找到最短路径。另外,到BFS结束,仍未算出最短路,这表明这两个点不连通。所以,连通性和最短路两个方法可以合一。

另外,涉及到图的操作,所以单独封装一个无向图类,把所有的关于增加、删除边,算最短路,找顶点或边等函数封装进去。基本上无需重构,只需增加新功能。

考虑到点的ID是不连续的,所以邻接表不能用传统的邻接表,必须用嵌套HashMap。

UML类图

image

复杂度分析

方法

Method ev(G) iv(G) v(G)
work.MyGraph.addPath(Path) 3.0 6.0 7.0
work.MyGraph.containsEdge(int,int) 2.0 2.0 3.0
work.MyGraph.containsNode(int) 1.0 1.0 1.0
work.MyGraph.containsPath(Path) 2.0 1.0 2.0
work.MyGraph.containsPathId(int) 1.0 1.0 1.0
work.MyGraph.getDistinctNodeCount() 1.0 1.0 1.0
work.MyGraph.getPathById(int) 2.0 1.0 2.0
work.MyGraph.getPathId(Path) 2.0 3.0 4.0
work.MyGraph.getShortestPathLength(int,int) 2.0 1.0 2.0
work.MyGraph.isConnected(int,int) 3.0 2.0 4.0
work.MyGraph.MyGraph() 1.0 1.0 1.0
work.MyGraph.removePath(Path) 2.0 6.0 7.0
work.MyGraph.removePathById(int) 2.0 2.0 3.0
work.MyGraph.size() 1.0 1.0 1.0
work.MyPath.compareTo(Path) 6.0 4.0 7.0
work.MyPath.containsNode(int) 1.0 1.0 1.0
work.MyPath.equals(Object) 5.0 3.0 5.0
work.MyPath.getDistinctNodeCount() 1.0 1.0 1.0
work.MyPath.getNode(int) 1.0 1.0 1.0
work.MyPath.getNodes() 1.0 1.0 1.0
work.MyPath.hashCode() 1.0 1.0 1.0
work.MyPath.isValid() 1.0 1.0 1.0
work.MyPath.iterator() 1.0 1.0 1.0
work.MyPath.MyPath(int...) 1.0 2.0 2.0
work.MyPath.size() 1.0 1.0 1.0
work.UndirectedGraph.addEdge(int,int,int) 1.0 1.0 1.0
work.UndirectedGraph.bfs(int) 1.0 4.0 4.0
work.UndirectedGraph.containsEdge(int,int) 1.0 2.0 2.0
work.UndirectedGraph.Pair.Pair(int,int) 1.0 1.0 1.0
work.UndirectedGraph.putMap(int,int,int) 1.0 3.0 3.0
work.UndirectedGraph.recalculate() 1.0 2.0 2.0
work.UndirectedGraph.removeEdge(int,int) 1.0 1.0 1.0
work.UndirectedGraph.removeMap(int,int) 2.0 3.0 4.0
work.UndirectedGraph.shortestPathLength(int,int) 2.0 3.0 3.0
Total 56.0 66.0 82.0
Average 1.65 1.94 2.41

Class OCavg WMC
work.MyGraph 2.29 32.0
work.MyPath 2.0 22.0
work.UndirectedGraph 2.25 18.0
work.UndirectedGraph.Pair 1.0 1.0
Total 73.0
Average 2.15 18.25

耦合度分析

Class Cyclic Dcy Dcy* Dpt Dpt*
work.MyGraph 0.0 8.0 16.0 1.0 1.0
work.MyPath 0.0 2.0 2.0 2.0 2.0
work.UndirectedGraph 0.0 1.0 1.0 1.0 2.0
work.UndirectedGraph.Pair 0.0 0.0 0.0 1.0 3.0
Total
Average 0.0 2.75 4.75 1.25 2.0

1.3 第十一次作业--在上次的图基础上,模拟地铁系统

这是我OO生涯的第二次翻车。可能和时间太紧张有关系,两次OO作业离得太近。不过,大部分原因在我自己,太过想当然,并查集都不去专门检查。不出所料,所有WA的点全部在这上面。细节决定成败。

不过,在讨论区的指导下,设计思路得到了极大的拓展。我开始甚至想用分支限界,后来一想,这是指数级别的。有大佬提示我用拆点,我就想到拆成完全图,结果超时超的一塌糊涂。最后,zyy奆佬想到了”站台模式“,豁然开朗,改完后时间缩短了70%多。

由于对并查集连通块个数考虑不周全(比如删除后怎么处理等),导致了bug。教训惨痛,永志不忘。(就要这种惨痛经历给我敲一下警钟,干啥都不能飘!)

架构上,明显的迭代开发。上次的graph类已经实现大部分功能,剩下四个方法只需要扩展这个类即可,所以直接extends,而不是复制粘贴graph类的源码。类头写成public class MyRailwaySystem extends MyGraph implements RailwaySystem。要加的东西还是不少的,还要重写add、remove方法以用于实现附加功能。但这样可避免影响之前已经完成的功能,还缩短了单个类的长度。

最短路就是迪杰斯特拉算法,优先队列优化,可以把时间复杂度降低到O(ElogE),对于稀疏图来说,比O(V^2)的一般实现要快。

UML类图

image

复杂度分析

方法

Method ev(G) iv(G) v(G)
work.MyGraph.addPath(Path) 3.0 6.0 7.0
work.MyGraph.containsEdge(int,int) 2.0 2.0 3.0
work.MyGraph.containsNode(int) 1.0 1.0 1.0
work.MyGraph.containsPath(Path) 2.0 1.0 2.0
work.MyGraph.containsPathId(int) 1.0 1.0 1.0
work.MyGraph.getDistinctNodeCount() 1.0 1.0 1.0
work.MyGraph.getId() 1.0 1.0 1.0
work.MyGraph.getPathById(int) 2.0 1.0 2.0
work.MyGraph.getPathId(Path) 2.0 3.0 4.0
work.MyGraph.getPaths() 1.0 1.0 1.0
work.MyGraph.getShortestPathLength(int,int) 2.0 1.0 2.0
work.MyGraph.isConnected(int,int) 3.0 1.0 3.0
work.MyGraph.MyGraph() 1.0 1.0 1.0
work.MyGraph.removePath(Path) 2.0 6.0 7.0
work.MyGraph.removePathById(int) 2.0 2.0 3.0
work.MyGraph.size() 1.0 1.0 1.0
work.MyPath.compareTo(Path) 6.0 4.0 7.0
work.MyPath.containsEdge(int,int) 1.0 1.0 1.0
work.MyPath.containsNode(int) 1.0 1.0 1.0
work.MyPath.equals(Object) 5.0 3.0 5.0
work.MyPath.getDistinctNodeCount() 1.0 1.0 1.0
work.MyPath.getNode(int) 1.0 1.0 1.0
work.MyPath.getNodes() 1.0 1.0 1.0
work.MyPath.getShortestPathLength(int,int) 1.0 1.0 1.0
work.MyPath.getUnpleasantValue(int) 1.0 1.0 1.0
work.MyPath.hashCode() 1.0 1.0 1.0
work.MyPath.isValid() 1.0 1.0 1.0
work.MyPath.iterator() 1.0 1.0 1.0
work.MyPath.MyPath(int...) 1.0 2.0 2.0
work.MyPath.size() 1.0 1.0 1.0
work.MyRailwaySystem.addEdge(int,int,int) 2.0 5.0 6.0
work.MyRailwaySystem.addNode(int,int) 1.0 2.0 2.0
work.MyRailwaySystem.addPath(Path) 2.0 2.0 3.0
work.MyRailwaySystem.containsPathSequence(Path[]) 1.0 1.0 1.0
work.MyRailwaySystem.dijkstra(long,int) 4.0 12.0 16.0
work.MyRailwaySystem.Edge.Edge(int,int,int) 1.0 1.0 1.0
work.MyRailwaySystem.getConnectedBlockCount() 1.0 4.0 4.0
work.MyRailwaySystem.getLeastTicketPrice(int,int) 5.0 4.0 6.0
work.MyRailwaySystem.getLeastTransferCount(int,int) 5.0 4.0 6.0
work.MyRailwaySystem.getLeastUnpleasantValue(int,int) 5.0 4.0 6.0
work.MyRailwaySystem.getRoot(int) 1.0 2.0 2.0
work.MyRailwaySystem.getTicketPrice(Path[],int,int) 1.0 1.0 1.0
work.MyRailwaySystem.getUnpleasantValue(Path,int,int) 1.0 1.0 1.0
work.MyRailwaySystem.getUnpleasantValue(Path,int[]) 1.0 1.0 1.0
work.MyRailwaySystem.getUnpleasantValue(Path[],int,int) 1.0 1.0 1.0
work.MyRailwaySystem.isConnectedInPathSequence(Path[],int,int) 1.0 1.0 1.0
work.MyRailwaySystem.Length.compareTo(Length) 1.0 1.0 1.0
work.MyRailwaySystem.Length.Length(int,long) 1.0 1.0 1.0
work.MyRailwaySystem.mixId(int,int) 1.0 1.0 1.0
work.MyRailwaySystem.mixId2NodeId(long) 1.0 1.0 1.0
work.MyRailwaySystem.MyRailwaySystem() 1.0 1.0 1.0
work.MyRailwaySystem.removeEdge(int,int,int) 4.0 3.0 6.0
work.MyRailwaySystem.removePath(Path) 1.0 2.0 2.0
work.MyRailwaySystem.removePathById(int) 1.0 2.0 2.0
work.MyRailwaySystem.unpleasantValue(int,int) 2.0 1.0 2.0
work.UndirectedGraph.addEdge(int,int,int) 1.0 1.0 1.0
work.UndirectedGraph.bfs(int) 1.0 4.0 4.0
work.UndirectedGraph.containsEdge(int,int) 1.0 2.0 2.0
work.UndirectedGraph.Pair.Pair(int,int) 1.0 1.0 1.0
work.UndirectedGraph.putMap(int,int,int) 1.0 3.0 3.0
work.UndirectedGraph.removeEdge(int,int) 1.0 1.0 1.0
work.UndirectedGraph.removeMap(int,int) 2.0 3.0 4.0
work.UndirectedGraph.shortestPathLength(int,int) 3.0 4.0 4.0
Total 107.0 128.0 160.0
Average 1.70 2.03 2.54

Class OCavg WMC
work.MyGraph 2.0625 33.0
work.MyPath 1.79 25.0
work.MyRailwaySystem 3.05 67.0
work.MyRailwaySystem.Edge 1.0 1.0
work.MyRailwaySystem.Length 1.0 2.0
work.UndirectedGraph 2.43 17.0
work.UndirectedGraph.Pair 1.0 1.0
Total 146.0
Average 2.32 20.86

耦合度分析

Class Cyclic Dcy Dcy* Dpt Dpt*
work.MyGraph 0.0 8.0 16.0 1.0 2.0
work.MyPath 0.0 2.0 7.0 2.0 3.0
work.MyRailwaySystem 0.0 9.0 20.0 1.0 1.0
work.MyRailwaySystem.Edge 0.0 0.0 0.0 1.0 2.0
work.MyRailwaySystem.Length 0.0 0.0 0.0 1.0 2.0
work.UndirectedGraph 0.0 1.0 1.0 1.0 3.0
work.UndirectedGraph.Pair 0.0 0.0 0.0 1.0 4.0
Total
Average 0.0 2.86 6.29 1.14 2.43

2、单元测试方法和JML回顾

2.1 JML

概述:JML语言是一种为Java程序设计的规格语言,遵循契约式设计规则。

编译:规格是用Java注释的方式编写的,因此可以被任意Java编译器编译。

思想:在保持可读性的同时,提供严格的形式语义。

作用:JML提供语义来严格描述Java模块的行为,防止模块设计者的设计错误。JML使用各种验证工具,例如运行时断言检查器和扩展静态检查器(ESC / Java),可帮助程序员进行开发。

`requires` pre-condition
`ensures` post-condition
`signals` exception-condition
`assignable` 定义了允许继续执行了后面方法的条件
`pure` 纯方法,不修改字段
`invariant` 定义了一个方法的不变的属性
`\result` 指向返回值
`\old(expression)` 用于表示进入方法之前表达式的值
`(\forall <decl>; <range-exp>; <body-exp>)` 全称量词
`(\exists <decl>; <range-exp>; <body-exp>)` 存在量词
`a==>b` a推出b
`a<==b` a隐含b
`a<==>b` a当且仅当b

2.2 构建一个单元测试(工具:JMLUnit)

创建如下Java文件(A-B示例)

package demo;

public class Demo {
    /*@ public normal_behaviour
      @ ensures \result == lhs - rhs;
    */
    public static int compare(int lhs, int rhs) {
        return lhs - rhs;
    }

    public static void main(String[] args) {
        compare(114514,1919810);
    }
}

生成测试类

image

运行一个测试

image

很显然,compare方法发生了溢出,所以遇到INT_MAXINT_MIN之类的就会fail。

这就是JMLUnit工具的使用方法。总结一下:

1、生成测试类

2、编译所有的Java文件,包括测试类

3、用openjml编译自己的jml

4、运行测试

3、bug分析

前两次比较简单,没怎么出现bug。

第三次,啊,具体说说,惨烈如战场

对连通块数的统计,采用了并查集的方法。并查集就是一棵树,所以根据图论,结点数减边数等于并查集数。但是,一个致命问题:由于并查集不支持删除,所以必须在remove之后重新构造并查集。所以,我在这里犯了错误,重新构造前还可能有add,这种情况没有clear()!果不其然,强测全WA在这里。

归根结底,还是心态不对,思考的时候一心二用,又想搞定这个方法又想搞定那个方法。如果分开思考,并且做好记录,就不会出现这么弱智的bug了。另外就是算法基础薄弱。但不管怎么说,能学到东西就不算白写。

谁没犯过错误啊,这都是正常的。但是,这么优质的一次作业被我糟蹋了,感觉好心痛,唉。

再说说互测(包括发现自己bug)的方法,自己写数据生成器是硬道理!颠扑不破啊,啥时候做伸手党要生成器都是最愚蠢的。互测开始后,一对拍立刻发现自己的问题所在!

另外就是Junit。啥时候手造数据都是很弱的,只有把随机性和精确性结合起来才能地毯式轰炸。

事不过三,希望下个单元不要再有这种问题了~

4、收获

第九次,学会了写容器类,特别是深刻体会了“不要自己造轮子”的真理。另外,根据规格写功能和异常处理,体会了类的鲁棒性。

第十次,复习了一下图论算法,连通性、最短路等。补上了数据结构的短板。集思广益啊,如果不是有人提示bfs,还在用dij或floyd呢,深刻体会到贪心思想的强大。

第十一次,仍然是最短路,学会了图的奇技淫巧,包括拆点法的使用等等;又复习了dij和并查集。教训越深刻,记忆越深刻,这次闪脱的教训不仅会伴随我的oo历程,更让我对开发过程有了新的认识。

最最重要的是,对JML规格的理解使用。对规格的阅读理解可以避免很多二义性的东西,使得设计更精确。就拿containsEdge方法来说,开始没有考虑自环问题,后来提交了一次,发现错在这里,就去研读规格,避免了误解题意。

最后特别感谢伦佬提供的强大测试工具。

OO最后的斗争,加油!

posted @ 2019-05-22 21:47  wancong  阅读(291)  评论(0编辑  收藏  举报