OO第三单元总结

OO第三单元总结

本单元是规格类作业,旨在对工程细节功能的实现,对编程逻辑的要求更高。

(一)JML理论基础

JML是一种行为接口规范语言,可用于指定Java模块的行为。JML能够更为精确地描述这些代码是做什么的,结合它首先可以更改或新增相应方法时降低引入bug的机会,其次也能够较为高效地发现和修正程序中的bug。且,在实际工程项目中,可以提供与应用程序代码完全一致的JML格式的文档。对基础JML表达式,在结合本单元资料和代码学习后可知:

1.原子表达式

\result 表示一个非void类型的方法执行所获得的结果,即返回值。

\old 表示一个表达式在相应方法执行前的取值。

\not_assigned(x,y...) 表示括号中的变量是否在方法执行过程中被赋值。

\not_modified(x,y...) 表示限制括号中变量在方法执行期间的取值未发生变化。

\nonnullelements(container) 表示container对象中存储的对象无null。

\type(type) 返回类型type对应的类型。

\typeof(expr) 返回表达式expr对应的准确类型。

2.量化表达式

\forall 对给定范围内的元素,每个元素都满足相应的约束。

\exists 对给定范围内的元素,存在某个元素满足相应的约束。

\sum 返回给定范围内的表达式的和。

\product 返回给范围内的表达式的连乘结果。

\max 返回给范围内的表达式的最大值。

\min 返回给范围内的表达式的最小值。

\num_of 返回指定变量中满足相应条件的取值个数。

(二)JMLUnit测试类

在学习参考了大佬的讲解和测试类代码后:

MyGraph graph = new MyGraph();
      MyPath path1;
      MyPath path2;
  
     public void setUp() throws Exception {
         int[] p1 = {1, 2, 3, 4, 5};
         int[] p2 = {-1, 3, 5, 6, 7};
         path1 = new MyPath(p1);
         path2 = new MyPath(p2);
         graph.addPath(path1);
         graph.addPath(path2);
     }
 
     public void tearDown() throws Exception {
     }
 
     public void testContainsNode() throws Exception {
     //TODO: Test goes here...
         Assert.assertEquals(1, graph.addPath(path1), 1);
         Assert.assertEquals(1, graph.addPath(path2), 1);
         Assert.assertTrue(path2.containsNode(-1));
         Assert.assertTrue(graph.containsNode(7));
         Assert.assertEquals(1, graph.addPath(path2), 1);
         Assert.assertTrue(graph.containsNode(5));
     }
 
     public void testAddPath() throws Exception {
         Assert.assertEquals(1,graph.addPath(path1),1);
         Assert.assertEquals(1,graph.addPath(path2),1);
     } 
     public void testRemovePath() throws Exception {
         Assert.assertEquals(1, graph.addPath(path1), 1);
         Assert.assertEquals(1, graph.addPath(path2), 1);
     }

 

(三)架构设计及代码实现

第一次作业

第一次作业主要依据已给出的JML规格进行代码的完善和补充。在MyPath类中equals方法的实现需注意强制转换;在MyPath类中compareTo方法需注意为字典序比较。在比完“大小”后,未返回则比较其size得到结果。在MyPathContainer中,对Path的存储设置了两个HashMap。一个Key,Value分别为Integer和Path,另一个则相反。主要目的在于便于两种方式的路径查找。

         

第二次作业

MyGraph中新增邻接矩阵、两点间距离矩阵和结点映射的HashMap。

private HashMap<Integer,Integer>  nodeMapping; // 结点映射
private int[][] adjMatrix = new int[250][250]; // 邻接矩阵
private int[][] disMatrix = new int[250][250]; // 两点间距离(1 或 infinite)

邻接矩阵来存新增(或移除)路径后相邻结点的连通情况。距离矩阵用来存两点间距离。若为infinite则说明两点不联通。在寻找最短路径时采用的是floyd算法。

public void update() {
    int maxSize = nodeMapping.size();
    setOne();
    for (int k = 0;k < maxSize;k++) {
        for (int i = 0;i < maxSize;i++) {
            for (int j = 0;j < maxSize;j++) {
                if (disMatrix[i][j] > (disMatrix[i][k] + disMatrix[k][j])) {
                    disMatrix[i][j] = disMatrix[i][k] + disMatrix[k][j];
                }
            }
        }
    }
}

 

第三次作业

此次的地铁系统要求增加:

·求最低票价

·求最少换乘次数

·求最低不满意度

·求连通块总个数

本次新增一个Edge类,用来存取两站点之间边的信息:length; price; unpleasant; transtimes。此类中包含更新边的方法,根据传入参数不同,共分为传入一条边,和传入两条边两种。

同时,利用边的权重赋值来对应不同的意义。以此求得四项。

(四)bug修复

最初写第一次作业时,因为对HashMap使用了解不深,最初仅使用ArrayList硬莽,遍历时循环时间复杂度很高……导致强测时间爆炸。因此debug时学习了HashMap,对储存方式进行重构。HahMap的查询不是遍历一个list,而是快速跳到数组的某个位置,只对很少的元素进行比较。这是HashMap查询快的原因。

private HashMap<Integer,Path> list = new HashMap<>();
private HashMap<Path,Integer> revlist = new HashMap<>();
private HashMap<Integer,Integer> distinctNodes = new HashMap<>();
private static int id = 0;
// use to find by id
// use to find by path

在第二次作业中,主要是在对数组更新时出现bug。尤其是remove后,对结点映射出的HashMap(nodeMapping)要进行remove,需要对整个邻接矩阵(adjMatrix)进行更新,而不是只对涉及到的path进行更新。同理需同步更新两点间最短距离矩阵(disMatrix)。缺少更新是导致该次作业出现bug的主要原因。

在第三次作业中,主要是在对边的信息更新上出现bug。对边的更新需有两种,依据是否有边和是否换乘,而不能混为一谈。在remove的时候更新重建图,而不在原有基础上改,避免越改越错orz。且经过一条边换乘次数需加一,细节很重要。

(五)规格撰写和理解上的心得体会

本单元的规格撰写和依据规格撰写代码,同时特点是对已有接口进行设计,这体现了架构的重要性。同时,在写这个单元作业时,也让我认识到工程化思想的重要性。即:对首先对可行性与需求进行分析,再进行系统设计和具体的程序设计。代码完成后再进行测试和维护。这种思想也能提高写代码和debug的效率,保证体系结构的稳定性。

 

posted @ 2019-05-22 15:58  TangLu85  阅读(243)  评论(0)    收藏  举报