OO第一單元總結
第一次作業:
第一次作業是單層括號的展開,總體來說難度重點在於如何進行表示式的解析。對於表示式我的處理是首先將單層的()**形式進行展開,然後再分析表示式、建樹、求值。對於建模部分我利用兩個數確定每個“基本”的因子,一個是係數一個是指數(現在看來這樣寫並不利於後續擴充套件),同時普通的因子也可以看作是一個表示式。我忽略了項那一步,認為最後的表示式是若干因子相加構成的。表示式是一個ArrayList,它裡面存的就是我的因子類,然後相鄰兩項利用加法連線。所以總體而言在建立完成表示式樹之後一切都變得相對簡單。對於精度問題我直接選擇BigInteger。
在bug方面由於作業要求較為清晰,情況也沒那麼多,所以我沒有被找到
第二次作業
第二次作業加入了三角因子與自定義函式,我對自己的因子進行了擴充套件,新增了SinCos類歸屬於普通因子內,對於一個普通的因子我定義成了a*x*(若干sin、cos相乘),同時在建樹設立節點是增加了對sin、cos、函式的分析。對於函式我只需要再將他的替換結果再次送入分析類中即可獲得他的表示式類,在這點上我的架構為我提供了不錯的便利,但是在因子的擴充套件以及化簡方面我的架構就十分的不友好。
在bug方面主要是如果呼叫的自定義函式內部含有三角函式的話,我的sin裡會有兩曾括號從而導致我的正則表示式提取不出有效資訊。而這個點直接讓我的強測一片慘淡。這也告訴了我面對一下規則較為複雜而且很有可能存在我想不到的情況是儘量慎用正則表示式。其次在互測內大家好像都是因為自定義函式處出現的錯誤,所以互測方面彼此並沒有發現
第三次作業
第三次作業無非就是在自定義函式和三角函式內部可以實現多層巢狀。我原先的架構只需要將我的()**放到最後設立節點時解析、在SinCos內部換為一個表示式類、重寫判斷化簡條件就完成了要求。只花了3個小時就結束了戰鬥。但這一次在化簡方面我做的較為複雜,對於sin**2+cos**2的情況,由於我要判斷內部的expr是否相等,這就需要我重新寫expr的equals方法,但是equals方法又依賴於我的sin或者cos內部是否相等。。。很明顯我需要設立當一個因子的Sin cos.size()為0的時候只需要係數和指數相等就可以了。之後從sincos到factor再到expr一次寫好判斷依據,就寫好了化簡。
在bug方面,由於第二次作業時保證不會出現sum(i**2)的情況而第三次卻又可能出現,但是我只注意了新增功能是否正確沒有管原先要求的變化,導致了這個新bug的產生。在互測時,我輸入了sin(0)**0成功抓到了一個人,但是我sum的這個bug被許多人抓到了。值得我說的是,我這一個點被幾個人反覆hack,著實是搞人心態。不過最好的方式是自己沒有bug,最好還是自己要多做測試啊。。。。
架構分析
由於我是一條路走到黑,我這裡就直接說我最終版的架構吧。我的架構並沒有太多的繼承、泛化,更多的是一個類一個類之間是一種包含的關係
我的部分方法複雜度太高,比如simplify函式由於我由於對hashmap等運用不熟,只好採用Arraylist,但用ArrayList時又怕記憶體的克隆出錯,只好運用多層迴圈,從而導致迴圈的複雜度較高,而呼叫simplify函式的複雜度則會更高。
下面是我從網路上找到的有關的名詞解釋:
名詞解釋:
ev(G)基本複雜度,是用來衡量程式非結構化程度的,非結構成分降低了程式的質量,增加了程式碼的維護難度,使程式難於理解。因此,基本複雜度高意味著非結構化程度高,難以模組化和維護。
iv(G)模組設計複雜度,是用來衡量模組判定結構,即模組和其他模組的呼叫關係。軟體模組設計複雜度高意味模組耦合度高,這將導致模組難於隔離、維護和複用。模組設計複雜度是從模組流程圖中移去那些不包含呼叫子模組的判定和迴圈結構後得出的圈複雜度,因此模組設計複雜度不能大於圈複雜度,通常是遠小於圈複雜度。
v(G)圈複雜度,是用來衡量一個模組判定結構的複雜程度,數量上表現為獨立路徑的條數,即合理的預防錯誤所需測試的最少路徑條數,圈複雜度大說明程式程式碼可能質量低且難於測試和維護,經驗表明,程式的可能錯誤和高的圈複雜度有著很大關係。
下面是我的類的耦合情況:
可見耦合度較高,這也是我之後要提升的。比如來說,我為了除錯時方便,在建立類時違背了private精神,設立了許多Getter和Setter函式,沒有使用建構函式,導致了在“上層類”中我需要很多行去將我解析出來的資訊填入類中,但是這樣一來帶來了暴露與耦合的問題。在聽課之後我知道這樣是不對的,下一單元我一定要認真改正。
下面是我的uml圖,由於沒有有效挖掘出他們之間的關係,於是在下列圖例中我採用了Assoication;
我的主要架構一般就是一個類的某些屬性就是另一個類,主要優點就是總體上看比較清晰易懂,對於程式的功能劃分比較清晰,輸入處理是輸入處理,建樹是建樹,取值是取值,化簡是化簡。每個步驟分開執行不僅有利於寫程式,更重要的事在尋找bug時通過除錯可以迅速定位大概在那一方面。
但是代價就是在改動或者除錯時十分困難,需要一層一層找到最下層。類與類之間存在著耦合嚴重的現象,有時出現bug我需要修改多個類。這樣容易造成修bug的同時造bug的情況,雖然這次我並沒有造成這樣的情況,但是不怕一萬,只怕萬一啊。
在我的架構內Sentence類是我的字串解析類,內設一個senExpr用來儲存我的表示式解析結果。具體而言就是首先建立類的樹,用樹的邏輯層次來表示計算的優先順序,之後再求值得到senExpr,之後就是我並不十分滿意的simplify。
而expr、factor、Sincos、func和sum則是我為了解析儲存“單位因子”而建立的類。他們的功能主要有兩個,一個是對輸入的字串進行解析併為自己的屬性新增相關資訊,其次就是返回自己的toString,而在解析的時候如果使用長段正則表示式很容易出現re,因此要謹慎選擇。
至於opNode、Node和ExprNode是我為了建樹而設立的類。從我第一次設立之後一直到最後我一直沒動過有關程式碼。
bug反思
有關於這幾次出現的bug,我認為主要有以下幾點原因。
首先是不同作業的要求會發生改變,我卻沒有及時關注到(sum(i**2)),之後我一定要經常注意這個問題。
其次是儘量少用一些特意化的正則表示式,一旦需求發生改變或者有某些情況發生改變,那麼很容易出現bug。
值得注意的是,在自己進行的測試裡,由於我的第二次作業仍然是依據大正則抓取sum函式,為了匹配到sum內可能含有的多層括號,我的正則表示式有很多“*”,本來測試是沒有問題的,但是在測試“sum(i,9998877564321,9998877564322,sin(i))"時由於”*“過多導致我的正則表示式在匹配時陷入了死迴圈,經過網路查閱這是正則表示式自己內部匹配的問題,因此,這種bug僅憑邏輯極難發現,只能依靠黑盒測試。
這些bug都是一些小bug,都是我沒有考慮到某些情況,這也是我不注重去特殊化的後果。
在測試別人bug時,我主要是根據要求構造一些邊界的BigInterger以及sin(0)**0這類資料進行測試。同時也將自己在測試自己程式時出現bug的用例來測試他人程式,比如
+-135*+x**5*7*(-287-x**2+x**0+x**3)**+2+(+x-978++987-+715)等等
心得體會
第一單元結束了,這一單元的訓練一方面增強了我的程式設計能力,鍛鍊了我面向物件的思維。同時也讓我明白了對於從過程中抽象出物件的重要性。面向物件對我也是一種挑戰。在之前我沒有接觸過java,同時對這種難度的題目沒有太多的接觸,每當在苦思很長一段時間之後想出思路後總覺得對自己是一次突破,我的收穫很大。但是同時我也應該注意到我的某些部分的處理並不完美,思路並不全面,這需要我在動筆之前靜下心來進行細細的又全面的思索。無論如何,我都希望在之後三個單元的練習中我能繼續得到鍛鍊,同時寫完之後出現的bug少一點,調bug真的太痛苦了。
總而言之,這單元作業讓我感受最深的是架構的重要性,一個好的架構不僅可以容易擴充套件,更可以減少bug的產生。其次,就是要減少程式設計時對於問題的特殊要求。當然還要注意高內聚低耦合的特點。下一單元的作業,我一定要著重注意這裡