面向物件課程第一單元總結
主要目標:對錶達式結構進行建模、計算和化簡。
1. 第一次作業
主要目標:通過對錶達式結構進行建模,完成單變數多項式的括號展開,初步體會層次化設計的思想。
1.1 基本思路
第一次作業原先是準備使用一般讀入模式,但由於對Advance中parse類的改寫失敗和時間的不足,中途轉戰預解析模式。
整個程式主要分為兩個大類,第一個是Main主類,主要實現表示式的讀取和計算化簡後的輸出。第二部分為Operation類,主要實現表示式樹的構建和表示式樹的計算與基礎化簡。
1.2 具體實現
第一次作業的實現方法較為簡單,主要點在於表示式樹的構建和計算。在Operation類中使用了兩個建構函式完成了表示式樹的構建,這是為了葉節點沒有更下層的左右節點而選擇的方法。在該類中也使用了兩個有差別的operate方法,分別對應兩個運算元和一個運算元的情況,最終通過getData的遞迴呼叫就可以獲得計算後的表示式結果。
1.3 UML圖
1.4 度量分析
1.4.1 方法複雜度分析
CogC
:認知複雜度
ev(G)
:基本圈複雜度
iv(G)
:設計複雜度
v(G)
:圈複雜度
methord | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
Operation.Operation(String) | 3.0 | 1.0 | 3.0 | 3.0 |
Operation.getData() | 4.0 | 3.0 | 4.0 | 4.0 |
Operation.operate(ArrayList, String) | 6.0 | 1.0 | 4.0 | 6.0 |
Operation.Operation(String, ArrayList) | 7.0 | 1.0 | 5.0 | 5.0 |
Main.main(String[]) | 24.0 | 1.0 | 12.0 | 12.0 |
Operation.operate(ArrayList, ArrayList, String) | 37.0 | 1.0 | 13.0 | 17.0 |
Total | 81.0 | 8.0 | 41.0 | 47.0 |
Average | 13.5 | 1.33 | 6.83 | 7.83 |
1.4.2 類複雜度分析
OCavg
: 平均操作複雜度
OCmax
: 最大操作複雜度
WMC
: 加權方法複雜度
class | OCavg | OCmax | WMC |
---|---|---|---|
Main | 11.0 | 11.0 | 11.0 |
Operation | 7.0 | 18.0 | 35.0 |
Total | 46.0 | ||
Average | 7.67 | 14.50 | 23.00 |
1.5 bug分析
本次作業程式碼存在一個bug,在資料處理時誤將一個地方的資料轉化為long型進行處理,導致資料溢位。
互測中發現兩人bug,兩人均是化簡過程中輸出錯誤。
2. 第二次作業
主要目標:通過對錶達式結構進行建模,完成多項式的括號展開與函式呼叫、化簡,進一步體會層次化設計的思想。
2.1 基本思路
第二次作業相較於第一次作業增加了三角函式,自定義函式和求和函式的處理。
整體思路分為兩大部分:首先是對自定義函式和求和函式的處理,我的選擇是在表示式解析前通過對字串的替換來解決該問題;第二部分為替換後表示式的解析與計算,通過Lexer和Parse等類逐步讀取表示式中的符號,並在解析過程中通過Poly類逐漸完成表示式的計算。
2.2 具體實現
2.2.1 自定義函式和求和函式的替換
自定義函式和求和函式的替換主要在Main類的operate函式中實現,通過迴圈字串找到自定義函式和求和函式的位置,並通過字串分割和字串替換的功能將代入轉換後的表示式替換到原表示式中,並返回到main主函式中。在此過程中需要注意求和函式替換i時避免sin中的錯誤替換。
2.2.2 解析、計算表示式
在這一部分,所有的表示式,項,因子均可定義到一個多項式類Poly類中,在Expr類和Term類中均有生成Poly類的建構函式,整個表示式的計算與化簡也基本在Poly類中完成。在Poly類中具體定義了一個HashMap的屬性,該HashMap的鍵為除係數外的所有因子,值為係數。在第二次作業中,與第一次作業不同的是除係數外不再僅僅只有x,而是增添了sin、cos的三角函式部分。對此,我的處理是新建了一個TriX類用來表示x和三角函式。
2.2.2.1 TriX類
在TriX類中,sin、cos也使用了HashMap用來儲存,其鍵值分別為三角函式內的表示式Mono與其乘方,而x的部分則使用BingInterger型別的xindex儲存其乘方數。除此之外,TriX類中的getCosString和getSinString方法用來返回化簡後的cos和sin函式。
2.2.2.2 Poly類
對於整個專案而言,我認為Poly類的重要性絕對是前二的。在這個類中主要實現了多項式的建立,計算和化簡。多項式的建立不必多言,而計算使用了多個方法共同完成。在乘法mul中,通過兩次迴圈逐一相乘並相加實現乘法的運算,並通過對多項式key值的比較實現基礎的同類項合併,而在乘方pow中,則是通過迴圈不斷地呼叫mul方法實現乘方的計算。多項式的化簡主要在toString方法中,這次實現的化簡方法較為簡單,只實現了x乘方數的特殊值化簡,仍然有很大的改進空間。
2.3 UML圖
2.4 度量分析
2.4.1 方法複雜度分析
methord | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
expr.Expr.addTerm(Term, String) | 7.0 | 1.0 | 5.0 | 5.0 |
expr.Expr.getPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Mono.equals(Object) | 3.0 | 3.0 | 2.0 | 4.0 |
expr.Mono.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Mono.Mono(BigInteger, BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Mono.toString() | 4.0 | 1.0 | 3.0 | 3.0 |
expr.Poly.add(Poly) | 4.0 | 1.0 | 3.0 | 3.0 |
expr.Poly.getPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Poly.getPolyNomial() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Poly.mul(Poly) | 39.0 | 1.0 | 12.0 | 12.0 |
expr.Poly.neg() | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Poly.Poly() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Poly.Poly(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Poly.Poly(BigInteger, BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Poly.Poly(Factor, BigInteger, String) | 4.0 | 1.0 | 3.0 | 3.0 |
expr.Poly.pow(BigInteger) | 4.0 | 1.0 | 3.0 | 3.0 |
expr.Poly.sub(Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Poly.toString() | 10.0 | 1.0 | 7.0 | 7.0 |
expr.Term.addFactor(Factor, String) | 10.0 | 5.0 | 6.0 | 6.0 |
expr.Term.getPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.TriX.equals(Object) | 3.0 | 2.0 | 4.0 | 4.0 |
expr.TriX.getCos() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.TriX.getCosString(String) | 9.0 | 2.0 | 4.0 | 5.0 |
expr.TriX.getSin() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.TriX.getSinString(String) | 9.0 | 2.0 | 4.0 | 5.0 |
expr.TriX.getXindex() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.TriX.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.TriX.isEmpty() | 1.0 | 1.0 | 3.0 | 3.0 |
expr.TriX.TriX(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.TriX.TriX(BigInteger, BigInteger, String, Boolean) | 8.0 | 1.0 | 6.0 | 6.0 |
expr.TriX.TriX(Map, Map, BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.getNumber() | 2.0 | 1.0 | 3.0 | 3.0 |
Lexer.getTri() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.next() | 4.0 | 2.0 | 4.0 | 5.0 |
Lexer.peek() | 0.0 | 1.0 | 1.0 | 1.0 |
Main.main(String[]) | 26.0 | 1.0 | 11.0 | 11.0 |
Main.operate(String, HashMap, ArrayList, ArrayList, ArrayList) | 99.0 | 1.0 | 32.0 | 36.0 |
Parser.parseExpr() | 4.0 | 1.0 | 5.0 | 5.0 |
Parser.parseFactor() | 25.0 | 5.0 | 14.0 | 14.0 |
Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.parseTerm() | 4.0 | 1.0 | 5.0 | 5.0 |
Total | 280.0 | 56.0 | 161.0 | 170.0 |
Average | 6.67 | 1.33 | 3.83 | 4.05 |
2.4.2 類複雜度分析
class | OCavg | OCmax | WMC |
---|---|---|---|
Parser | 4.75 | 12.0 | 19.0 |
Main | 23.5 | 36.0 | 47.0 |
Lexer | 2.0 | 5.0 | 10.0 |
expr.TriX | 2.27 | 6.0 | 25.0 |
expr.Term | 3.5 | 6.0 | 7.0 |
expr.Poly | 3.0 | 12.0 | 36.0 |
expr.Mono | 2.0 | 3.0 | 8.0 |
expr.Expr | 3.0 | 5.0 | 6.0 |
Total | 158.0 | ||
Average | 3.76 | 10.63 | 19.75 |
2.5 bug分析
本次作業未能進入互測房間,bug來源於對 "\t" 的替換並未實現,在中測2中掛掉一個點,強測中掛掉兩個點,修復gaibug後強測通過。
3. 第三次作業
主要目標:通過對錶達式結構進行建模,完成多層巢狀表示式和函式呼叫的括號展開與化簡,進一步體會層次化設計的思想。
3.1 基本思路
第三次作業相較於第二次作業增加了多層巢狀,由於第二次作業已經基本滿足第三次作業要求,故只有少量改動。
3.2 具體實現
第三次作業基本和第二次作業相同,只對連個小地方做了更改。
3.2.1 自定義函式和求和函式
由於本次作業支援函式的部分巢狀,因此在函式替換這一部分增加了while判斷,直到表示式中不存在自定義函式和求和函式時才退出迴圈,並且將函式的替換拆分為了幾個更小的方法,便於理解。
3.3.2 三角函式
本次作業的另一修改便是將第二次作業中三角函式部分的key值由Mono修改為String型別,在Parse解析時直接將化簡後的表示式傳入三角函式的內部。
3.3.3 表示式化簡
第三次作業在表示式化簡上更近了一步,對三角函式的特殊值,係數的特殊值,乘方的特殊值都進行了一定的處理替換,使得最後的表示式更加簡潔,並且為了避免替換後仍然存在可化簡部分,main主方法內部對整個程式又重新跑了一次。
3.3 UML圖和度量分析
本次作業與第二次作業沒有太多改變,省略此部分。
3.4 bug分析
本次作業測出一個bug,三角函式的化簡替換出現問題,程式執行第二遍時出現無法處理的6*1**3,最後答案會輸出錯誤值216,修改方案只需在特殊值替換時外層套上一層括號。
互測中發現兩個bug,一個是求和函式部分出現超大資料是會爆int,另外 一個是求和函式替換後出現的1**6無法解析處理。
4. 架構設計體驗
4.1 優缺點
三次作業優缺點都很明顯,部分類較為臃腫,可以更加細緻地拆分為更小的類,部分地方的處理十分粗糙,還有許多的改進空間,例如化簡部分,沒有對三角函式進行更多的化簡,丟失了許多的效能分。優點就是第二次作業的開發較為成功,在第三次作業的時候幾乎沒有太多的改動。
4.2 架構迭代
由於第一次作業選擇了預解析模式,第二次作業寫得十分痛苦,完全地把第一次作業推倒進行了重構,第三次作業在第二次的基礎上進行了較小的迭代,更改了少量地方就完成了任務。
4.3 心得體會
面向物件課程的第一次作業就已經給我帶來了很大的壓力,在沒有太多基礎的情況下第一次的一般讀入開發以失敗告終,最終在第二週經過系統性的重構才能夠完成基礎的任務。只能說這是一個很痛苦的過程,但也的確學到了很多東西,每一次的重新思考都會有一些收穫,覺得這樣弄可能會讓程式碼更加的簡潔明瞭,也會更適合後續的迭代開發。但我還是認為課程的難度分佈不是十分地均勻,第一次作業從0開始的難度過大,而第三次作業迭代開發的難度相對有較小,希望能夠稍微調整一下每次作業的難度分佈。