2018-北航-面向對象-前三次OO作業分析與小結


基於度量的程序結構分析

由於平時使用了NetBrains出品的IDEA作為IDE,在分析程序的時候我使用了IDEA的插件Metrics Reloaded。然而在使用時發現不懂得很多分析項目的含義,因此花費了一些時間了解,並在這里總結。

Complexity Metrics(復雜度分析)

這部分我們需要使用的主要是方法和類的復雜度分析。

方法的復雜度分析主要基於循環復雜度的計算。循環復雜度是一種表示程序復雜度的軟件度量,由程序流程圖中的“基礎路徑”數量得來。

  1. ev(G):即Essentail Complexity,用來表示一個方法的結構化程度,范圍在$[1,v(G)]$之間,值越大則程序的結構越“病態”,其計算過程和圖的“縮點”有關。

  2. iv(G):即Design Complexity,用來表示一個方法和他所調用的其他方法的緊密程度,范圍也在$[1,v(G)]$之間,值越大聯系越緊密。

  3. v(G):即循環復雜度,可以理解為窮盡程序流程每一條路徑所需要的試驗次數。

對於類,有OCavgWMC兩個項目,分別代表類的方法的平均循環復雜度和總循環復雜度。

Dependency Metrics(依賴度分析)

依賴度分析度量了類之間的依賴程度。有如下幾種項目:

  1. Cyclic:指和類直接或間接相互依賴的類的數量。這樣的相互依賴可能導致代碼難以理解和測試。
  2. Dcy和Dcy:計算了該類直接依賴的類的數量,帶表示包括了間接依賴的類。
  3. Dpt和Dpt:計算了直接依賴該類的類的數量,帶表示包括了間接依賴的類。

第一次作業

類圖如下:

這次作業比較簡單,當時也很很努力的用來OO思想。但從算法角度來說效率比較低。除了面向對象對象思想之外,還學習到了正則表達式和異常處理。

第二次作業

類圖和度量分析圖如下:

Method ev(G) iv(G) v(G)
"Building.Building(int,int)" 1 1 1
"Building.getMaxFloor()" 1 1 1
"Building.getMinFLoor()" 1 1 1
"ERequest.ERequest(int,double)" 1 1 1
"Elevator.Elevator(Building,double,double)" 1 1 1
"Elevator.getFinishTime(int,double)" 1 1 1
"Elevator.gotoFloor(int,double)" 1 3 3
"FRequest.FRequest(int,double,String)" 1 1 2
"FRequest.getDirection()" 1 1 1
"FRequest.isSame(Object)" 2 1 2
"Request.Request(int,double)" 1 1 1
"Request.getFloor()" 1 1 1
"Request.getTime()" 1 1 1
"Request.isIllegal(int,double,Building)" 1 3 3
"Request.isSame(Object)" 4 1 4
"Request.parse(String,Building)" 5 3 5
"RequestQueue.RequestQueue(Building)" 1 1 1
"RequestQueue.finishRequest()" 1 1 1
"RequestQueue.getRequest()" 1 1 1
"RequestQueue.inQueue(Request)" 3 2 3
"RequestQueue.isEmptyQueue()" 1 1 1
"RequestQueue.isIllegal(Request,double)" 4 7 7
"RequestQueue.readAll()" 12 8 12
"RequestQueue.updateNext()" 1 1 1
"RequestQueue.updateUntil(double)" 1 4 4
"Scheduler.Scheduler(Elevator,Building)" 1 1 1
"Scheduler.command()" 1 3 3
"Scheduler.main(String[])" 1 1 1

由於程序本身比較簡短,度量工具看不出太多的問題。程序的耦合狀況還可以。主要是用於輸入的RequestQueue.readAll()的復雜度較高,可以通過將方法進行拆解,提高可讀性。

我將兩種請求繼承自一個Request父類,而在最近閱讀《Java核心技術》時,我發現可以在Request類中增加一個工廠方法來進行字符串的解析,而不是把字符串的解析放到請求類本身的構造函數中。

第三次作業

類圖和度量分析圖如下。

Class OCavg WMC Cyclic Dcy Dcy* Dpt Dpt*
ALS_Scheduler 3.5 21 0 10 10 0 0
Building 1 3 0 0 0 5 8
ERequest 1 2 2 1 5 2 7
Elevator 1.75 21 0 4 6 3 4
FRequest 1.5 6 2 3 5 3 7
HitchHikerQueue 2.22 20 0 5 9 1 1
Movable.Direction n/a 0 0 0 0 5 8
Request 1.88 15 2 3 5 7 7
RequestQueue 2.25 27 1 3 8 3 3
Scheduler 2.5 10 1 6 8 2 3

限於篇幅沒有給出方法的度量,但是可以發現復雜度較高的方法有HitchHikerQueue.getNextFloor()RequestQueue.readAll()ALS_Scheduler.isHitchHikable()。這三個方法的作用分別是得到在考慮捎帶請求的情況下的下一個停留樓層,讀入所有請求,以及判斷當前請求是否可以被捎帶。其中讀入請求的方法不可避免,而另外兩個方法則由實現方法導致。不過仔細檢查之后確實發現了getNextFloor()方法可以簡化的地方。

這次作業的思路是由請求隊列RequestQueue讀入和保存當前正在等待的請求,增加一個HitchHikerQueue類保存正被捎帶的類,電梯類Elevator則保存了當前運動信息,具有有輸出功能,Scheduler負責調度。

這次作業的設計基本延續上次的思路,為了努力達到OO思想(實際上是偷懶符合符合直觀),我設計了等待請求隊列,捎帶請求隊列和帶有所有運動信息和輸出功能的電梯。但這樣也給我帶來了很多麻煩。原因如下:

  1. 將三個隊列分開來使得時間信息被打亂,尤其是會有同時間但是先后順序不同的請求。一開始我以為只要三個隊列保持各自的順序就行了,實際上后期對拍發現會有更加復雜的情況。因此我后來給每個請求都加上了時間戳,每次對隊列進行修改都需要重新排序一次。現在回想,其實可以隊列用兩個優先隊列實現,保存相同的元素,但是一個以時間戳作為key,另一個以樓層作為key(因為我的設計中需要不斷找出下一個最近的樓層)。
  2. 因為指導書中提到用電梯的toString()輸出,而且為了更好的OO,我把保存運動信息,輸出狀態,判斷捎帶的部分功能都移到了電梯中,又大大增加了復雜度。
  3. 出現了一些語法錯誤,例如在foreach語句中不能刪除所迭代對象的元素。不過這些錯誤倒是幫助我學習到了一些關於迭代器,lambda的知識

互測部分

說實話,三次作業拿到的代碼都不算太好,還是比較面向過程的。三位同學都采用了一個個掃字符串處理輸入而不是正則表達式反向匹配,而且在之后電梯的代碼量又較大,讓我很難把代碼讀進去。

因此,在測試過程中,我主要是把自己的測試數據或者對拍來找出對方的錯誤,然后調試程序找到錯誤點,找到原因並進一步發掘更多的錯誤點。

第一次作業

對方:第一位同學主要出錯在數據處理上,比較原始的字符串處理方式難免的弊端。

我方:這次作業是我唯一被測出問題的一次,問題在於對指導書的理解有問題。我將指導書所說的不輸出零項理解為了不輸出次數為0的項,從而造成錯誤。

第二次作業

對方:第二位同學則是由於INVALID格式的問題,錯誤樹上幾乎被掛滿。但我還是找出了一些邊界條件上處理的錯誤,這位同學把關門時刻的請求當做了非同質請求,十分可惜。另外,他的輸入在某些情況下會導致指針越界,是使用原始數組的弊端了。

我方:這次沒有被測出Bug

第三次作業

對方:第三位同學在我一系列時間的對拍后都未出現錯誤,倒是代碼中通過以0.5s為粒度的模擬方式比較有意思。雖然比較低效,但是使得程序難以出錯。

我方:這次沒有被測出錯誤

總結

這三次作業中,我對第三次作業比較不滿意。一是因為開始寫代碼之前沒有想清楚,造成后來拆東牆補西牆。另外這次作業還是有一些算法的意思在里面,以我的實現方法,就需要一個高效的數據結構,然而當時只是為了讓程序跑起來,並沒有想太多。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com