2022 BUAA OO 第一單元總結
2022 BUAA OO 第一單元總結
前言
在第一單元的學習過程中,我最大的感受是在面對巨大的程式碼量的工作和重複迭代更新的需求之下,對於程式設計能力能力較弱的同學(just like me),掌握面向物件設計的模式是尤為重要,但是從面向過程到面向物件的這一轉變也是一個較大的挑戰。這一單元可以說是既注重了架構的設計,也對處理字串的程式碼能力進行了考察。
雖然通過了第一單元的測試,但是我在第二次作業的強測中由於輸出過程中一個bug的疏忽而掛掉了不少強測測試點,這是這一次作業我最遺憾的一個點,希望能在接下來的作業中,能戒驕戒躁,深刻反思此次的錯誤,以後避免再次出現類似的低階bug。
下面我將從作業架構分析、程式bug分析、單元設計體驗和心得體會
一、作業架構分析
通過此單元作業的練習,我認識到了對於每個單元的第一次作業,如何初步建立一個具有魯棒性和擴充套件性的架構是至關重要的,這就相當於以後三次作業的“地基”。
在第一次作業開始之前,我也做了預習作業和第一單元的實驗作業,學習到了一些架構設計的方法,比如工廠法等等。對於第一次作業我嘗試使用正則表示式對字串進行解析,同時嘗試使用一些SimpleTerm、SimpleExpr的對Term和Expr進行統一化處理的形式進行化簡和輸出,雖然這樣設計可能不是很優秀,因為我在迭代的過程中進行了一些大的改動,可以算是在區域性進行了重構操作,但是這樣的架構可能也有一些優點和值得繼續思考的點,為此記錄一下,方便日後的反思、總結。
三次作業的UML類圖及架構分析、程式碼分析
首先,本次第一單元的三次作業基本沿用了第一次的架構,但是在第二單元的設計過程中,由於發現原來的一個類設計的不合理、擴充套件性弱,無法適應第二單元的需求只能進行大改,因此重新設計了該類,可以算是一次比較大的區域性重構工作。PS:類中測試使用的入口方法就不展示在UML圖中了。
第一次作業
架構UML圖及實現方法分析
作業的思路分為四個步驟:解析->儲存->化簡->輸出
第一次作業我採用了自頂向下的解析方法,設計了ExprAnalyse類用來逐層解析字串,首先輸入一個要解析的字串,之後呼叫ExprAnalyse的analyseExpr方法,用正則表示式匹配的方式對錶達式進行逐層的解析,其中把Expr的符號和第一個Term的符號都歸到第一個Term裡面,這樣只有Term和帶符號數字是有符號的
為了儲存這些解析出來的內容,設計了Expr、Term和Num、Variation四個類,其中Num和Variation均是Factor介面的實現。這樣就將剛才解析的內容儲了起來,形成了Expr-Term-Factor三層結構。
之後是比較令人頭疼的化簡部分,由於表示式中的項又含有符號整數、變數等諸如”-100*x*x*+123“等非常不整齊的樣子,所以我就想到了可以將每一項都化簡成為”[符號]係數*x**指數“的形式,併為這種形式新建一個類名為SimpleTerm,這樣就可以把每一項統一成一個形式。這樣我們化簡的思路就大概有了:統一形式->去括號->合併同類項。這個思路我一直沿用到第三次作業。
其中,統一形式由Term來實現,去括號主要由SimpleTerm中的項乘法和Expr中的表示式乘法實現,合併同類相主要由SimpleTerm中的項的加法和Expr中的表示式加法實現。最後都歸結到一個SimpleExpr中進行輸出。
架構的優缺點:
優點:
此次作業的優點是我基本把如何完成任務的思路理清楚了,而且建立了比較清晰的層次結構,這個整體的思路在後面兩次作業中都沒有變化過,避免了大規模的重構,但是在後面需求增長時還是對一個SimpleExpr這個不合適的類進行了區域性性的重構。
缺點:
首先,沒有考慮到後面巢狀括號的情況,所以在Factor的解析過程中對正則表示式的運用是有侷限性的,降低了可擴充套件性。
其次,在進行輸出的時候,選擇了在SimpleExpr中寫一個printAll方法進行整體輸出,而不是在每一個因子中寫一個toString方法進行”化整為零“的輸出,這就導致了後續新增因子時需要增加SimpleExpr的printAll方法,使得這一方法非常冗長,也就導致了後來我對該類的一個區域性重構。這一點也讓我吸取了教訓,應該在類的設計過程中,如果一個複雜的任務需要由多個類完成,最好的方式是把任務分配給每一個涉及到的類,讓每一個類都呼叫自己的方法,最後用一個類整合所有的方法,而不是使用一個類的一個方法獨立完成。這也讓我體會到了面向物件設計的優雅之處。
基於度量分析第一次作業的程式結構:
程式碼規模:
類的OO度量:
方法的複雜度度量:
第一次作業
架構UML圖及實現方法分析
第二次作業的需求相比第一次作業有了更大的升級,增加了如下幾個方面的內容:
1.括號巢狀
2.新增自定義函式因子,不可遞迴、不可巢狀
3.新增sum求和因子,不可以遞迴、不可巢狀
4.新增了sin、cos三角函式,其中內容是一個帶符號整數或者變數因子
此次作業的難度應該是這三次作業中難度最大的一次,因為需求量暴增對於第一次作業沒有處理好的部分是一次巨大的挑戰。
為了應對括號巢狀問題,我先在解析表示式和化簡得過程中使用了遞迴,使得在解析表示式因子的過程中再新建一個ExprAnalyse物件,達到可以解析巢狀括號的表示式的目的,而在化簡得過程中,化簡表示式也採用了遞迴處理。
對於新增的自定義函式因子和sum求和因子,先建立了Function和Sum類進行儲存,之後可以用”字串替換“的方式進行替換,不過此處我不是直接對字串進行替換,而是在解析的過程中,如果遇到了自定義表示式呼叫或者sum求和因子再進行替換,這就可以在遵循原表示式結構不變的基礎之上進行替換,經測試這樣bug其實還是比較少的。
由於又涉及到了三角函式這個基本因子,因此我在化簡得過程中,在SimpleTerm類裡面又新增了一個HashMap用來儲存三角函式物件以及對應的指數值。
優化
對於優化部分,我重寫了一下hashCode和equals方法,因此可以採用hashmap的直接檢索具有相同內容的三角函式物件進行相乘,以及檢索具相同的SimpleTerm物件進行相加。不過其實重寫hashCode方法具有一定的風險,很容易出一些隱形的bug,這一點跟助教討論後得知可以直接使用java自帶的hashCode更加方便和安全。
缺點
此次作業的缺點也比較地明顯,因為我在寫SimpleTrigoTerm的時候發現他其實和SimpleExpr差不多,但是後來由於時間的侷限,就沒有進行改動因此導致了程式碼的重複和冗餘比較多,同時由於我重寫了hashCode,有一些疏忽的地方也為後續的迭代埋下了雷。
基於度量分析第二次作業的程式結構
程式碼規模
類的OO度量
方法的複雜度度量
第三次作業
架構UML圖及實現方法分析
有了第二次作業設計的基礎第三次作業其實就比較簡單一點了,主要涉及到如下幾個點:
1.自定義函式呼叫的實參可以是自定義函式呼叫或者sum
2.Trigo的內容可以是一個表示式因子
主要在升級的過程中,進一步給表示式因子重寫hashCode,這樣在計算Trigo的hashCode的時候可以直接呼叫,更加方便。在化簡的過程中依然沿用第二次作業的思路。
但是由於第二次作業我寫的比較冗餘,所以在第三次作業就把SimpleExpr這個類刪除了,直接用StdTrigoTerm來代替,同時在每一個需要輸出的類內重寫了toString方法,方便最後的輸出。
缺點
呼叫的函式比較多,在遞迴呼叫時使用的記憶體空間較大,在計算多層巢狀時的速度會比較慢。
基於度量分析第二次作業的程式結構
程式碼規模
類的OO度量
方法的複雜度度量
二、bug分析
此次作業的bug主要集中在了第二次作業中。
第一次作業bug:中測通過,強測通過,互測0bug
第二次作業bug:中測通過,強測掛了6個point,互測被hack了5次
第三次作業bug:中測通過,強測掛了1個point,互測0hack
其中,第二次作業主要的bug是在輸出的時候,本來應該判斷hashMap中對應的係數輸出,而當時錯誤地判斷成了hashMap中的SimpleTerm的係數,由於在合併同類項的時候係數改變並沒有同步更新到SimpleTerm中,所以就導致了最後輸出的錯誤,造成強測和互測的失利。
在測試程式的過程中,我採用的手搓資料的方法,雖然效率比較低,但是可以保證每一個手搓的資料都比較有針對性,可以發現一些具有特色的奇怪bug。
三、單元設計體驗和心得體會
對於程式設計能力不是很強的我來說,這一單元可謂是一個字:難!難在一開始思路就不好搞清楚,難在程式設計過程中對字串處理有一些細節的地方需要花時間去做到位。
但是評價此單元作業的第二個字我覺得是:值!真的學到了不少的東西,比如hashMap、hashCode的使用,比如如何設計類,讓不同的類各司其職,不至於某一個類太過於複雜,從而降低程式碼的複雜度,增加可維護性和可擴充套件性。體會到了面向物件的設計就像是藝術一樣精妙。
希望在接下來的OO課程學習過程中再接再厲,更上一層樓。