目錄java
本次架構分爲如下幾個部分。python
官方輸入接口提供的UmlClass
等只提供了元素的id,name,類型等信息,不能方便地查詢對應的信息。正則表達式
所以,分別封裝了UmlClass
,UmlInterface
,UmlOperation
.算法
類MyClass
封裝了UmlClass
,存放class信息、父類、實現的接口、屬性、操做、關聯。編程
類MyInterface
和以上相似。sass
MyOperation,存放operation和對應的參數。安全
因爲須要根據類名查詢相應的類,須要容器提供根據名字查詢元素的方法。bash
這部分的代碼主要將分散的元素創建聯繫。架構
主要實現交互接口的方法。編輯器
整體架構基本不變。
相比上次,增長了容器,存儲順序圖、狀態機。
MyInteraction
,存儲順序圖信息和對應的lifeline, message等。
MyStateMachine
,存儲狀態圖信息和對應的state, transition。
MyState
存儲狀態信息和後繼狀態。
本次做業依次須要檢查如下規則:
R001:針對下面給定的模型元素容器,不能含有重名的成員
R002:不能有循環繼承
R003:任何一個類或接口不能重複繼承另一個接口
第1條規則:查詢並統計容器裏的元素。
第2和第3條規則:使用圖論算法。增長MyClassGraph
類用於規則檢查。將元素組織成圖:類或接口做爲節點,接口實現關係、繼承關係做爲有向邊,創建有向圖。使用tarjan算法求出循環繼承,使用廣度優先遍歷判斷是否有重複繼承。
第1單元整體架構爲解析器、因子、項、表達式。可是因爲每次做業須要解析的表達式定義不一樣,須要重寫解析器。
主要架構爲生產者-消費者模型。
可是這一單元筆者未能設計良好的架構,致使每次做業都要推翻原來的架構,代碼複雜度上升。
架構設計:路徑MyPath
、容器MyPathContainer
、圖MyGraph
、算法和底層圖、MyRailwaySystem
不足之處在於,沒有將圖的底層結構、建模過程、算法分離,致使單個類複雜度太高。官方的參考程序使用了工廠模式,將建模過程分離出來,值得借鑑。
架構設計:以上已經說明,再也不重複。
在實現算法的代碼中,面向過程的寫法仍然存在,可是應用面向對象的方法,實現代碼重用、下降單個模塊的複雜度。從這4個單元下來,筆者深入體會到面向對象方法比純粹的面向過程相比,有很大的進步,體如今封裝、複用等方面。
這一單元的測試以黑盒測試爲主。數據生成器的編寫用到了遞歸降低。
SinFactor := 'sin' '(' Factor ')' ('^' Number)? CosFactor := 'cos' '(' Factor ')' ('^' Number)? PowerFactor := 'x' ('^' Number)? ConstFactor := Number ExpressionFactor := '(' Expression ')' Factor := PowerFactor | SinFactor | Cos | ExpressionFactor | ConstFactor Term := Factor ('*' Factor)* | '+' Factor ('*' Factor)* | '-' Factor ('*' Factor)* Expression := ('-'|'+')? Term ('-'|'+' Term)*
使用sympy
比較輸出的表達式是否正確。
另外,對於格式錯誤的輸入判斷,筆者測試了\f
,\v
等空白字符。同時,使用Python生成較長的表達式,用來測試使用複雜正則表達式來匹配整個字符串的程序。因爲Java的正則表達式使用了NFA,在正則表達式過於複雜的狀況下,會發生棧溢出。
這一部分的測試仍然是黑盒測試。可是,因爲在沒有發生線程競爭的狀況下,很容易保證正確性,筆者沒有編寫輸出檢查器。這一單元的bug最主要是線程安全的問題,有可能出現的狀況是程序死循環、死鎖或提前退出。因此筆者只檢查了程序的運行狀況;因爲線程安全問題難以重現,須要改變CPU頻率等運行環境並重複測試同一組數據。事實證實這樣簡單的測試仍是使人滿意的,在互測環節發現了一些線程安全的問題。
這一單元是JML的使用,筆者可以找到的開源JML工具鏈,暫時只有Openjml能兼容JAVA 8。然而OPENJML尚不成熟,在檢查複雜程序時會出現內部錯誤。使用jmlunitng自動生成的測試代碼只檢查很是廣泛的邊界,例如int的最大值和最小值,忽視了JML規格。
因此這一單元的本地測試筆者使用了Junit單元測試+黑盒測試。
互測環節,仍然使用黑盒測試。編寫數據生成器比較簡單。可是,編寫檢驗輸出是否正確的程序,至關於重寫一遍做業,沒法保證正確性。所以,筆者用python編寫了數據生成器,使用的測試方法是,將組內的程序放在一塊兒,輸入同一組數據,比較輸出是否相同。若是出現不一樣或超過規定的cpu時間,那麼必定有錯誤。
缺點是無法保證正確性;因爲輸入數據較爲複雜,在測試數據組數較少的狀況,難以全面覆蓋,效率低下。
以本單元最後一次做業爲例子,筆者編寫了以下的bash腳本,事先將程序編譯打包爲jar,使用100組隨機數據進行測試,耗時2-3小時:
export CLASSPATH=specs-homework-3-1.3-raw-jar-with-dependencies.jar:jfxrt.jar for cnt in `seq 100` do echo testing $cnt python3 gen.py > in.txt for i in Assassin Berserker Caster Lancer Rider Saber do ulimit -S -t 35 echo $i case $i in Assassin) java -cp $i.jar:$CLASSPATH worknine.Main < in.txt > $i.out;; Berserker) java -cp $i.jar:$CLASSPATH homework.Main < in.txt > $i.out;; Caster) java -cp $i.jar:$CLASSPATH subway.Main < in.txt > $i.out;; Rider) java -cp $i.jar:$CLASSPATH MainClass < in.txt > $i.out;; *) java -cp $i.jar:$CLASSPATH Main < in.txt > $i.out;; esac ulimit -S -t unlimited done for i in Assassin Berserker Caster Lancer Rider Saber do diff -u Caster.out $i.out > /dev/null if [ $? -ne 0 ] then cp in.txt in_$cnt.txt echo $cnt $i failed fi done done
這一單元的本地測試筆者沒有使用單元測試。因爲UML結構複雜,筆者並無寫數據生成器,而是手動構造數據。
經驗教訓是測試的時候,不能忽視coverage,這能夠避免一些低級錯誤。筆者在最後一次做業中,誤將InteractionDuplicatedException
寫成InteractionNotFoundException
,然而在本地測試中沒有測試充分,未能發現這個bug。
總的來講,這4個單元下來,測試的方法都是以黑盒測試爲主,使用單元測試儘量保證測試全面。
首先是代碼能力的提高。每次做業,代碼量都在500行以上,最後一次做業的代碼量甚至達到了2000行,對編碼能力提出了很高的要求。
最大的收穫是,面向過程、面向對象、函數式編程等編程思想,對筆者來講是分析、解決問題的不一樣角度,而不是對立關係。
其中,SOLID原則對設計合理的代碼架構幫助很大,很大程度上加強了可拓展性、下降了debug的複雜度。
另外,從數據流的角度,筆者也使用了Java 8的stream來簡化代碼,替換掉複雜的循環。
關於課程
關於課程網站
全部實驗
和全部做業
位置對調。