面向對象編程:第四單元博客做業

第四單元博客做業

本單元兩次做業的架構設計

第1次做業

本次架構分爲如下幾個部分。python

基礎部分

官方輸入接口提供的UmlClass等只提供了元素的id,name,類型等信息,不能方便地查詢對應的信息。正則表達式

所以,分別封裝了UmlClass,UmlInterface,UmlOperation.算法

MyClass封裝了UmlClass,存放class信息、父類、實現的接口、屬性、操做、關聯。編程

MyInterface和以上相似。sass

MyOperation,存放operation和對應的參數。安全

容器

因爲須要根據類名查詢相應的類,須要容器提供根據名字查詢元素的方法。bash

解析器

這部分的代碼主要將分散的元素創建聯繫。架構

交互接口

主要實現交互接口的方法。編輯器

第2次做業

整體架構基本不變。

相比上次,增長了容器,存儲順序圖、狀態機。

MyInteraction ,存儲順序圖信息和對應的lifeline, message等。

MyStateMachine ,存儲狀態圖信息和對應的state, transition。

MyState 存儲狀態信息和後繼狀態。

規則檢查

本次做業依次須要檢查如下規則:

R001:針對下面給定的模型元素容器,不能含有重名的成員

  • 針對類圖中的類(UMLClass),其成員屬性(UMLAttribute)和關聯對端所鏈接的UMLAssociationEnd不能有重名

R002:不能有循環繼承

  • 只考慮類的繼承關係、類和接口之間實現關係,以及接口之間的繼承關係。所謂循環繼承,就是按照繼承關係造成了環。

R003:任何一個類或接口不能重複繼承另一個接口

  • 該規則考慮類之間的繼承關係、接口之間的繼承關係,以及類對接口的實現關係,包括直接繼承或間接繼承

第1條規則:查詢並統計容器裏的元素。

第2和第3條規則:使用圖論算法。增長MyClassGraph類用於規則檢查。將元素組織成圖:類或接口做爲節點,接口實現關係、繼承關係做爲有向邊,創建有向圖。使用tarjan算法求出循環繼承,使用廣度優先遍歷判斷是否有重複繼承。

架構設計及OO方法理解的演進

架構設計

Unit 1

第1單元整體架構爲解析器、因子、項、表達式。可是因爲每次做業須要解析的表達式定義不一樣,須要重寫解析器。

Unit 2

主要架構爲生產者-消費者模型。

可是這一單元筆者未能設計良好的架構,致使每次做業都要推翻原來的架構,代碼複雜度上升。

Unit 3

架構設計:路徑MyPath、容器MyPathContainer、圖MyGraph、算法和底層圖、MyRailwaySystem

不足之處在於,沒有將圖的底層結構、建模過程、算法分離,致使單個類複雜度太高。官方的參考程序使用了工廠模式,將建模過程分離出來,值得借鑑。

Unit 4

架構設計:以上已經說明,再也不重複。

面向對象方法

在實現算法的代碼中,面向過程的寫法仍然存在,可是應用面向對象的方法,實現代碼重用、下降單個模塊的複雜度。從這4個單元下來,筆者深入體會到面向對象方法比純粹的面向過程相比,有很大的進步,體如今封裝、複用等方面。

測試理解與實踐的演進

Unit 1

這一單元的測試以黑盒測試爲主。數據生成器的編寫用到了遞歸降低。

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,在正則表達式過於複雜的狀況下,會發生棧溢出。

Unit 2

這一部分的測試仍然是黑盒測試。可是,因爲在沒有發生線程競爭的狀況下,很容易保證正確性,筆者沒有編寫輸出檢查器。這一單元的bug最主要是線程安全的問題,有可能出現的狀況是程序死循環、死鎖或提前退出。因此筆者只檢查了程序的運行狀況;因爲線程安全問題難以重現,須要改變CPU頻率等運行環境並重複測試同一組數據。事實證實這樣簡單的測試仍是使人滿意的,在互測環節發現了一些線程安全的問題。

Unit 3

這一單元是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

Unit 4

這一單元的本地測試筆者沒有使用單元測試。因爲UML結構複雜,筆者並無寫數據生成器,而是手動構造數據。

經驗教訓是測試的時候,不能忽視coverage,這能夠避免一些低級錯誤。筆者在最後一次做業中,誤將InteractionDuplicatedException寫成InteractionNotFoundException,然而在本地測試中沒有測試充分,未能發現這個bug。

總結

總的來講,這4個單元下來,測試的方法都是以黑盒測試爲主,使用單元測試儘量保證測試全面。

本身的課程收穫

首先是代碼能力的提高。每次做業,代碼量都在500行以上,最後一次做業的代碼量甚至達到了2000行,對編碼能力提出了很高的要求。

最大的收穫是,面向過程、面向對象、函數式編程等編程思想,對筆者來講是分析、解決問題的不一樣角度,而不是對立關係。

其中,SOLID原則對設計合理的代碼架構幫助很大,很大程度上加強了可拓展性、下降了debug的複雜度。

另外,從數據流的角度,筆者也使用了Java 8的stream來簡化代碼,替換掉複雜的循環。

給課程的改進建議

關於課程

  1. 關於指導書和做業要求細節,但願在公佈以前就肯定好細節問題,而不是等同窗反饋問題後再修改做業要求
  2. 但願可以經過其餘途徑(例如電子郵件)通知學生,而不是隻經過課程網站這個單一渠道。
  3. 但願課程組可以和其餘課程(例如:操做系統、組合數學)協調,合理安排做業的時間節點,下降同窗們在其餘課程的壓力。
  4. 但願實驗課和理論課的時間錯開,而不是上午理論課,當天下午實驗課。

關於課程網站

  1. 但願Markdown編輯器支持表格等功能。
  2. UI方面建議將全部實驗全部做業位置對調。
相關文章
相關標籤/搜索