---恢复内容开始---java
[CSDN博客连接](https://blog.csdn.net/weixin_43387647/article/details/90451173) @[toc] ## 1、JML语言的理论基础及应用工具链 #### (一)定义: JML(Java Modeling Language):对Java程序进行规格化设计的一种表示语言。JML是一种行为接口规格语言 (Behavior Interface Specification Language,BISL),基于Larch方法构建。BISL提供了对方法和类型的规格定义 手段。所谓接口即一个方法或类型外部可见的内容。 #### (二)契约式设计核心思想: 软件系统中的元素之间相互合做以及“**责任**”与“**义务**” 1. 指望全部调用它的客户模块都保证必定的进入条件:这就是函数的**先验条件**—客户的义务和供应商的权利,这样它就不用去处理不知足先验条件的状况。 2. 保证退出时给出特定的属性:这就是函数的**后验条件**—供应商的义务,显然也是客户的权利。 3. 在进入时假定,并在退出时保持一些特定的属性:**不变式**。method A(){ assert(前置条件) do something assert(后置条件) return }
具体使用详见《JML Level 0手册》node
很遗憾,电脑不兼容openJML,按照教程严格执行,却一直失败:git
public int hashCode() { int[] array = new int[3]; array[0] = min; array[1] = max; array[2] = pathId; return Arrays.hashCode(array); }
@Test public void hashCodeTest() { assertNotEquals(new Edge(-69,54,1).hashCode(), new Edge(-68,23,1).hashCode()); //Values should be different. Actual: -34843 HashSet<Integer> set = new HashSet<>(); for (int i = -50; i < 50; i++) for (int j = -50; j < 50; j++) { Edge edge = new Edge(i, j, 1); set.add(edge.hashCode()); } assertEquals(set.size(), 5050); //java.lang.AssertionError: // Expected :2704 // Actual :5050 i!=j: (100+1)*100/2 }
public int hashCode() { return (min + "," + max + "," + pathId).hashCode(); }
@Test public void getLeastTransferCountTest() { Path path = new MyPath(1,2,3); MyRailwaySystem system = new MyRailwaySystem(); system.addPath(path); path = new MyPath(2,4,5,6); system.addPath(path); path = new MyPath(3,5); system.addPath(path); try { assertEquals(system.getLeastTransferCount(1,6),1); //java.lang.AssertionError: //Expected :2 //Actual :1 } catch (Exception e) { e.printStackTrace(); } }
根听说明信息,发如今dijstra更新过程当中出现错误,在bug修复处分析了具体缘由及解决方式。github
本单元做业要求依次实现四个类:算法
其中,MyPathContainer是MyPath的容器,而且实现了MyGraph的部分功能。所以,MyGraph中以MyPathContainer做为组成元素之一。同理,MyRailwayStation将MyGraph做为组成元素之一。各种包含关系以下:
设计模式
在后续迭代过程当中,不对前面实现过的类进行修改,仅是扩展添加新的属性及方法(遵循OCP原则:面对需求,对程序的改动是经过增长新代码进行的,而不是改变原来的代码。)数组
本次做业比较关键的几个方法为:网络
DISTINCT_NODE_COUNT
时都从新计算一遍,所花的时间复杂度是不能承受的。最开始我就是直接遍历,致使第一次强测爆40private HashMap<Integer, Integer> nmap = new HashMap<>(); //node的容器<node,number(node出如今各个路径中的次数)>
DISTINCT_NODE_COUNT
时直接返回nmap.size();
o(v+e)
,对全图搜索复杂度为o(v(v+e))
private HashMap<Integer, HashSet<Integer>> linkMap; private HashMap<Integer, HashMap<Integer, Integer>> disMap; private HashMap<Integer, Edge> edgeMap;//hashCode(edge)-->edge(a,b)
o(v+e)
Node
,组成链表结构:是四元组<node(Integer),value(Integer),edge(Edge),next(Node)>for(Integer edgeHashCode: linkMap.get(arrive.getNode()))
Class | Cyclic | Dcy | Dcy* |
Dpt | Dpt* |
---|---|---|---|---|---|
Edge | 0 | 0 | 0 | 3 | 5 |
Main | 2 | 2 | 6 | 2 | 3 |
MyGraph | 2 | 3 | 6 | 1 | 3 |
MyGraphTest | 0 | 0 | 0 | 0 | 0 |
MyPath | 0 | 0 | 0 | 2 | 4 |
MyPathContainer | 0 | 0 | 0 | 1 | 4 |
MyRailwaySystem | 2 | 4 | 6 | 2 | 3 |
MyRailwaySystemTest | 0 | 2 | 7 | 0 | 0 |
Node | 0 | 1 | 1 | 1 | 4 |
均在正常范围内数据结构
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
MyGraph.isConnected(int,int) | 4 | 2 | 5 |
MyGraph.nodeEdgeOk(int) | 4 | 3 | 4 |
MyRailwaySystem.getLeast(int,int,int) | 5 | 3 | 5 |
MyRailwaySystem.renewNodeMap(int,int) | 3 | 13 | 14 |
Node.add(Node) | 5 | 6 | 6 |
其中MyRailwaySystem.renewNodeMap
方法,即对于单源的dijstra方法长50行,为了兼容3个搜索,分支不少,而错误也正是出如今这里。架构
之后任意一个方法,行数最好不要超过30行,秉承SRP(单一职责原则),只作出一个行为,而后经过封装和组合达到最终实现行为的目的。
(前面已经测试过)
最后发现node=6处value为2,实际上value应该为1
在HashMap数据结构中,若是访问的key值不在其中,函数会返回null,而不是抛出异常,然后对null调用函数会报错NullPointerException
。所以在这里经常出现诡异的bug,之后使用get时要当心,更好的方式是使用getOrDefault
,没有key值就给出默认的数据。
此外,对于HashMap套HashMap现象,连续使用两次get(),会形成意想不到的错误,不如先将乱序节点经过一个HashMap映射到从1开始的正整数上,而后直接使用二维静态数组。
JML是一种先进的契约式设计模式,将设计和实现不可跨越的鸿沟大幅减少。大致上解决问题分为从需求到设计,从设计到实现两个过程。而JML规范地描述了代码须要执行的约束条件,能够说是一种LLR(Low-Level Request)。
实际上,我的感受JML更偏向于需求,而非设计。由于不论是你须要具体的数据结构,仍是算法,都须要我的亲自去定义。而且为了描述一个需求,用大段大段的代码去规范和描述也不是一个简单的任务。极可能须要多层抽象,甚至有时根本找不出一个描述需求的严格的表达方式。写规格自己都要比实现一个需求难上许多。
可是比较受用的是自动测试生成JUnit,对类中的某一个具体的函数进行测试。虽然直接在控制台输入输出也颇有效,但那样测完一次就结束了,没有办法对测试集进行管理。也没有办法在扩展修改了类之后保证类的兼容性。
总之,这一个单元是至关革新的,业界缺少成熟的代码规格化设计方式,网络上对契约式设计也鲜有听闻,可是之后可能会日渐成熟。
---恢复内容结束---