---恢復內容開始---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,對類中的某一個具體的函數進行測試。雖然直接在控制檯輸入輸出也頗有效,但那樣測完一次就結束了,沒有辦法對測試集進行管理。也沒有辦法在擴展修改了類之後保證類的兼容性。
總之,這一個單元是至關革新的,業界缺少成熟的代碼規格化設計方式,網絡上對契約式設計也鮮有聽聞,可是之後可能會日漸成熟。
---恢復內容結束---