BUAAOO第三单元总结

---恢复内容开始---

JML 语言

 理论基础

  JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。JML是一种行为接口规格语言Behavior Interface Specification LanguageBISL),基于Larch方法构建。它在Java代码中增加了一些符号,这些符号用来标识一个方法是干什么的,却并不关心它的实现。如果使用JML的话,我们就能够描述一个方法的预期的功能而不管他如何实现。通过这种方式,JML把过程性的思考延迟到方法设计中,从而扩展了面向对象设计的这个原则。
  那么添加这些标记语言有什么好处呢:

  • 能够更为精确地描述这些代码是做什么的
  • 能够高效地发现和修正程序中的bug
     
  • 可以在应用程序升级时降低引入bug的机会
     
  • 可以提早发现客户代码对类的错误使用
     
  • 可以提供与应用程序代码完全一致的JML格式的文档
     

 应用工具链

  OpenJML:OpenJML is a program verification tool for Java programs that allows you to check the specifications of programs annotated in the Java Modeling Language.

  JMLUnit:For each Java(TM) or JML source file, and for the top-level class or interface defined in that source file with the name of the source file, the commmand jmlunit, or the graphical user interface version jmlunit-gui , generates code for an abstract test oracle class, that can be used to test that class or interface with JUnit.

JMLUnitNG/JMLUnit测试用例

我使用了IDEA自带的插件进行简单的测试,简单流程如下

选择Test

一下是初始化和对size()的简单测试

import com.oocourse.specs2.models.Graph;
import com.oocourse.specs2.models.Path;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class MyGraphTest {

    @Before
    public void setUp() throws Exception {
        int[] p1 = {1, 2, 3};
        int[] p2 = {1, 2, 3, 6};
        int[] p3 = { 2, 3, 4, 5};
        Path path1 = new MyPath(p1);
        Path path2 = new MyPath(p2);
        Path path3 = new MyPath(p3);
        Graph graphA = new MyGraph();
        graphA.addPath(path1);
        graphA.addPath(path2);
        graphA.addPath(path3);
        System.out.println("begin");
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("end");
    }

    @Test
    public void size() {
        Assert.assertEquals(1, graph.addPath(path1), 1);
    }

最后完成测试

 

程序架构

 第一次作业

第一次作业是让我们完成MyPath和MyPathContainer的构建。对MyPath的架构三次作业没有任何变化,使用普通int数组完成nodelist的存储,同时增加一个Hashmap用于存储不同的node,使得containsNode函数和getDistinctNodeCount函数的复杂度降到O(1)。所有函数实现起来都不难。

对MyPathContainer的实现,使用了三个Hashmap存储所有数据,分别是HashMap<Path, Integer> pmap,HashMap<Integer, Path> pidmap,HashMap<Integer, Integer> nodemap,三个map在add和remove时进行更新。pmap和pidmap使的查询是否有path和获取path的复杂度降低,而nodemap是用于统计所有不同的点的个数而使用。

 第二次作业

第二次作业是让我们完成MyPath和MyGraph的构建。对于MyPath没有任何改动。

对MyGraph,由于需要查询边,因此增加edgemap来用于查询。而对于查询两点是否连接的需求,我是用并查集操作,新增connectmap来对构建连接图,只要两个点的value相同就可以判断为连接。而对于getShortestPathLength函数,构建邻接表,使用广度优先遍历得到。以上几个map在每次add和remove的时候都会重新构建,且最短路径对每个点都会求出来,所以每次查询的复杂度都为O(1),但是在add和remove时候复杂度会非常高。

具体实现见下图

  第三次作业

第三次作业是让我们完成MyPath和MyRailwaySystem的构建。MyPath多了getUnpleasantValue函数,但并没有大改动。

对MyRailwaySystem的构建,新增的getConnectedBlockCount函数直接在并查集的基础上统计有多少不同的value值就行了。而对于最少换乘,最低票价,最低不满意度,其实就是构建三个带不同权值的无向图,最少换乘图就是同一条Path的各个点都相连并设置权值为1,求出的结果减1。最低票价就是同一条Path各个点间算出需要的最低票价并加2就是权值,求出结果减2。最低不满意度就是同一条Path各个点间算出最低不满意度并加32就是权值,求出结果减32即可。这次我没有使用邻接表,而是构建了邻接矩阵,将各个点的id映射到邻接矩阵中,然后使用迪杰斯特拉算法求出最短路径,然后就得到了各个值。我也是在每次add和remove时就构建并算出全部的点对应的各项数据。

这三次作业一二次是直接叠加,但是第三次由于前两次考虑欠妥,重构过多,使得结构异常臃肿。

具体实现如下图

 

 

BUG修复

第一次作业的compareTo函数出现问题,因为我在比较的时候使用了两数相减和0比较,没有考虑溢出的情况,导致有较大的数据时就会产生错误的结果,因此我改成两数直接用if比大小就可以了。

第二次作业由于我将equal直接使用==代替,在比较两个点是否连接时,由于我的map的value为Integer类型,我直接使用==,在数据量大时会判定两个值相等的value为不相等,从而造成isConnected函数出错,间接造成getShortestPathLength出错,这个错误也使得我强测只拿了60分。

第三次作业我没有考虑当两个点相同时,价格和不满意度都返回0,我返回了错误的值导致错误。

心得体会

JML给我们带来的不仅仅只是一种规范化的语言,更是对整个架构的规范和思考,从怎么做到做什么,这是一种更高的层次。同时,利用JML和我们所写代码的结合,我们能进行更加高效和规范化的测试。然而我在这三次作业中做的还是太少,对JML的代码仅仅在于理解层次,用来方便写自己代码,对整个架构的理解还是太过于肤浅,这也导致了代码的复用性差,第三次重构的多,代码比较臃肿冗余,还有许多方面仍有欠缺。

---恢复内容结束---

posted @ 2019-05-21 20:28  Therp-GY  阅读(198)  评论(0编辑  收藏  举报