OO第一单元总结博客
OO第一單元作業總結
? ? ? ? 本單元的三次作業內容為多項式求導。第一次作業中,表達式支持因子形式為帶符號整數,以x為底數的冪函數。第二次作業中,表達式因子形式在第一次作業的基礎上,支持標準三角函數(sin(x)、cos(x))為底數的冪函數。第三次作業中,表達式因子進一步支持三角函數嵌套形式以及用小括號包裝的表達式。
一、程序結構分析
1.1 三次作業類的規模分析
(一)
第一次作業中,我總共運用兩個類完成任務,各因子屬于Poly類,表達式屬于Polynomial類。
(1)
Poly類代碼35行。Poly類中,屬性in、ex分別代表因子的系數與指數。類中四個方法,構造方法兩行,calculate方法十行,index、exp方法各一行。calculate方法中,有兩個控制分支,用于區分指數為0或非0時的求導方法。
class Poly {private BigDecimal in; //系數private BigDecimal ex; //指數public Poly(BigDecimal n,BigDecimal m) {} //構造方法public Poly calculate() {} //因子求導public BigDecimal index() {} //提取系數public BigDecimal exp() {} //提取指數}(2)
Polynomial類中代碼135行,兩個屬性 indexList、expList分別為表達式項的系數列表與指數列表。類中4個方法。initNew方法用來處理輸出,初始化表達式,該方法56行,總共7個分支用于解析不同輸入格式的表達式中的項以及判定非法輸入。me方法5行,對讀入表達式進行合并同類項,進行化簡。derive方法48行對表達式中的項進行求導,最終輸出結果。main函數45行,對console中字符串預處理,化為理想形式,初步判斷輸入字符串是否合法。
public class Polynomial {private static LinkedList<BigDecimal> indexList = new LinkedList<>();//項的系數列表private static LinkedList<BigDecimal> expList = new LinkedList<>(); //項的指數列表private static void initNew(String line0) {} //拆分字符串,初始化表達式private static void me(BigDecimal[] exp, BigDecimal[] index, int[] mark) {}// 化簡private static void derive(BigDecimal[][] poly, Poly[] element) {} //求導函數public static void main(String[] args) {} }(3)第一次作業OO類圖以及性能度量
?①類圖
?②性能度量
可以看出,在Polynomial類中derive方法,讀入字符串初始化表達式的initNew方法耦合度高,且圈復雜度高,維護難度較大。
(二)
第二次作業中,我總共運用了三個類。Factor類記錄因子,Term類記錄由乘號將因子鏈接起來組成的項,Polynomial類記錄項由加減號鏈接起來組成的表達式。
(1)
Factor類有兩個屬性,co為指數,facType為底數類型。該類110行。
兩種構造方法分別針對不同構造需求設計,構造方法1為初始化構造,2行,構造方法2服務后續化簡合并同類項操作,7行。getConstant、facDiff、printFactor方法均為單項因子求導相關函數分別占5行,33行、8行,分別具有2個、4個、2個條件分支。same、addCo、resetCo、equal、subtract方法服務后續化簡,均占1行。
class Factor {private BigInteger co; //指數private String facType; //類型public Factor(BigInteger co,String str) {} //構造方法1,用輸入拆分構造public Factor(Factor b) {} //構造方法2,用factor賦值構造public BigInteger getConstant() {} //求導時提取指數public void facDiff(ArrayList<Factor> part) {} //單因子求導,將求導結果添加到ArrayList中public String printFactor() {} //得到單因子的導數public boolean same(Factor b) {} //比較兩因子類型相同public BigInteger addCo(Factor b) {} //兩個類型相同的因子指數相加public void resetCo(BigInteger newInt) {} //重置因子指數public boolean equal(Factor b) {} //判斷兩個因子完全相同public Factor subtract(Factor b) {} //因子類型相同,指數相減,得到新的因子 }(2)
Term類有兩個屬性,con表示項中常數因子,facList是項的因子序列。
Term類總共400行,其中Term()為構造方法,termDifferent()、printTerm()方法分別用來單項求導以及最后輸出渠道結果的方法。其余方法都是用來化簡的方法。
經過分析OO性能,可看出構造方法的基本復雜度、耦合度、圈復雜度都較高,主要因為構造項時直接將字符串傳入構造方法,沒有構造parser類對字符串預處理。printTerm() 方法耦合度較高,圈復雜度大主要是在輸出求導結果時,為盡量減少輸出的符號因為存在很多循環,條件分支較多。canCom(),br(),canSub(),canSubNew()幾個方法都是用來化簡表達式的,主要思路是對于包含多個項的表達式中的兩個項,判斷是否有利用三角函數的性質化簡的空間,如果有,則對原表達式進行更改。在這幾個方法中,用的編程手段比較原始,故復雜度較高,優化空間其實很大。
(3)
Polynomial 類有三個私有屬性,orTerms保存讀入表達式, diff保存初步求導表達式結果,last保存對初步求導結果化簡后得到的表達式。Polynomial 類總共260行。
可以看出,用于找到表達式中第一個系數大于0的項便于化簡的方法基本復雜度高,非結構化程度高。求導后,合并同類項的方法combineDIff耦合度高。
(三)
第三次作業的總體思路是遞歸“剝殼”,將表達式用樹結構表示,葉子結點是無嵌套、加減、乘法包裝的原子因子。在第三次作業中,我總共用了6個類,一個接口。Nest類、Add類、Mul類均繼承Combine類,并繼承CombineElement借口。Factor類代表常數,x的冪函數,sin(x)的冪函數、cos(x)的冪函數四種原子因子。Combine 類匯總了表達式中出現的嵌套、加減法、乘法運算中,遞歸分離孩子、判斷包裝類型、判斷合法性等方法。
(1)
Combine類有四個屬性,Combine類left表示左孩子,express屬性表示該結點的表達式,combineType表示該結點的包裝類(嵌套or加減or乘),brank熟悉表示該結點是否被括號包裹。Combine類270行。
?
Combine類構造方法耦合度高,圈復雜度高,主要原因是未對字符串進行處理,直接被我粗暴地投入構造方法中,在構造過程中分支極多,多出輸出WF,退出程序,可以說這個操作很不優美。defineType方法用來判斷結點包裝類型,得到結點combineType,為進一步將結點分配給Add類、Mul類、Nest類做準備。在該方法中,多次使用循環增加了復雜度。
(2)
Add類主要負責被加減法包裝的結點。除Combine類的屬性外,還有right屬性代表Combine類的右孩子、Addtype屬性代表加法還是減法。Add類90行。
根據Add類OO性能表可以看出,用于分離左右孩子的方法getChild()基本復雜度較高,圈復雜度較高,主要原因可能是求左右孩子的過程中調用StringBuilder,拼接字符串,增加了復雜度。
(3)
Mul類主要負責被乘號包裝的結點。除父類的屬性外,還有Combine類的right代表右孩子。該類72行。
(4)
Nest類主要負責嵌套包裝的結點。由于嵌套類支持指數,該類除繼承父類屬性外,還有coeff屬性代表指數項,nestType屬性表示嵌套類型(sin或者cos)。該類108行。
觀察OO性能列表,發現求原子因子導數的方法facDiff基本復雜度高,圈復雜度高,主要因為在該方法中,我采用了switch根據facType條件選擇,在沒有個case中,又需要根據sym屬性區別返回的導數類型,分支極多。其實比較系統化、工程化的處理方式應該是對常數、x、sin(x)、cos(x)單獨建立類,它們繼承葉子結點類,分別具有求導方法。
? ? ?Main類對輸入進行預處理,利用遞歸構造樹,求導輸出。該類160行。
(7)第三次作業類圖
1.2 基于三次作業類規模分析的總結
分析三次作業的OO性能圖,發現自己第一單元寫代碼時存在如下問題:
?這三次作業中的進步:
總的來說,我這三次作業的代碼還很“面向過程”,在對比了同學優秀代碼后,我發現自己的代碼設計中許多操作仍然很“幼稚”,希望在接下來的幾個單元中代碼風格會逐漸成熟起來。
二、程序bug分析
2.1 第一次作業bug分析
第一次作業初次上手,最大的困難是在處理輸入。
剛開始思路不清晰,沒有想到對輸入字符串進行去空格等預處理,對于matcher的group方法不熟悉,導致我第一版代碼在init方法中羅列將近10中正則表達式,出現了大端條件分支,判斷表達式是否合法,分割每一項。第一版代碼在本地測試中不堪一擊,漏洞百出,主要原因是窮舉法根本不可能遍歷每一種合法的形式與非法形式,一旦出差錯,輕則輸出結果錯誤,重則程序報錯。
重構第二版代碼時,考慮到對輸入字符串進行預處理,將清爽的字符串透給init方法。
private static void initNew(String line0) {String line = line0;Pattern pattern = Pattern.compile("^([+-])([+-])?(\\d+\\*)?(x)" +"(\\^[+-]?\\d+)?|^([+-])([+-]?\\d+)");Matcher match = pattern.matcher(line);while (match.find()) {...line = match.replaceFirst("");match = pattern.matcher(line);}if (line.length() != 0) {System.out.print("WRONG FORMAT!");System.exit(0);}}第一次作業在強測中性能分吃虧,但是沒有錯強測點以及互測點。性能分吃虧是因為沒有注意性能要求是輸出長度,所以沒有把第一項挪成正系數項。
2.2 第二次作業bug分析
有了第一次作業處理輸入的思路,第二次作業相對得心應手。
第二次的重點是優化(不優化真的不會有問題),丟人的講,運用十分原始的手段合并項的時候主要干的事情是講表達式的TermList傳給方法,在Term類的方法中對比TermLIst中的成員與從TermLIst的下標參數中傳過來的另一個成員比較是否除需要合并額外處理的因子不同外,其余因子都相同。在這個過程中,我犯了兩個錯誤,第一個錯誤是我在雙重循環中直接對TermLIst進行刪除,增加元素,收到了Idea的警告。第二個錯誤,也是后來我被hack很慘的錯誤,是我只單方向判斷了一個Term的因子另一個Term全包含,忘記反過來判斷另一個Term的因子是否也被本Term全包含,就類似于充要條件被我弄成了充分條件。
第二次作業被hack的另一個點是輸出格式問題。第二次作業中,我力求優化,對于輸出*1這種沒有考慮清楚,結果輸出了+*這種不倫不類的東西。
第二次作業成也優化,敗也優化。優化畢竟是更高的追求,要做好必然是要更細心的。
2.3 第三次作業bug分析
第三次作業相對復雜一些,在我構造遞歸結構的時候,每一個類輸入形式都是很理想化的,這就需要在構造數的每一層級時想清楚自己當下的代碼需要滿足的條件,這個條件是否需要其他類中處理滿足。顯然,這一點沒有做好,我前后思路脫節,導致后續自己本地測出各種bug,到最后也沒用修復完全。
比如,我的處理中,最開始原子類沒有支持-x,-sin(x)這種形式,導致我提交之前猛然發現我-x導求出來都是錯的。又比如:在對(-(表達式))這個結構處理時,我是將-變成了(-1*(表達式)),同時我預處理的時候要把兩個連續的正負號合并成一個符號。然而悲慘的我沒有注意到兩個處理應該注意先后順序,導致我最后在強測點有一個輸出了WF,因為沒有識別出(++sin(x))。
加減法包裝的類最開始我很輕視,認為是最簡單的,實際上在后來測試中,發現加減法類考慮不完善,后患無窮。最嚴重的錯誤莫過于沒有想到*+\\d以及^+\\d這種形式,直接導致combine類判斷類型出錯以及放到Add類中分離孩子出錯。在減法求導時,要不要給右孩子的導數整體加括號又是門學問,比如x-x+x顯然不同于x-(x+x)。最開始對Add類的輕視讓我在后期debug時苦不堪言。不幸的是,由于我的粗心,我最后提交的時候并沒有改全,還是被hack了。
三、狼人策略
第一次互測,人生地不熟的我很佛系的隨便試了試數據,希望找到求導求錯的,但是本組中并沒有人把我設的弱弱的導求錯。最后我只是提交了一個自己最后才想到的WF的坑,x^+ 2,果然砍了2個人。
第二次互測中,由于第一次沒有被砍,我還是不知道怎么找錯,最后找出來的錯仍然是局限于沒有判斷出WF。第二出互測中,我集中觀察了像我一樣預處理的同學的預處理代碼,發現了一些沒有注意的空格以及加減號合并的問題,找到了一些可乘之機。
第二次作業被砍得很慘,發現自己代碼中遍及WF,求導求錯,輸出格式錯誤的問題。第三次作業互測中,我牢記自己第二次被砍出的錯誤,試圖挖掘同屋中輸出格式錯誤,算錯這樣的錯誤。嘗試了長輸入,大致沒有發現問題后,我轉向短小復雜的輸入形式,我相信第三次作業難就難在解析表達式的細節處理。果然,最后我構造的-(sin((-x))-((x)+(x^+2))),-(+-(2*x)),sin(-12)這樣的短式子沒有砍空。另外,我觀察了一下同屋同學包裝類型的處理,遺憾的發現,他們的這些類的構造方法中,對于輸入的要求并沒有我自己的設計中那樣苛刻,所以本地測試錯誤的點,用在他們身上都失靈了。
四、Applying Creational Pattern
這三次作業,雖然我逐漸嘗試更加“面向對象”一些,但是自己的代碼仍然十分“面向過程”。對于接口,繼承,只在第三次作業中嘗試使用,但也正如上文所說,很多完全可以用繼承優化實現的地方,我仍然很“質樸”地羅列著case。
另一方面,構造方法被我粗暴地投喂原始字符串也不是明智之舉,應該考慮構造字符串處理類,把字符串加工出廠后再投喂給構造方法。
?
?
?
轉載于:https://www.cnblogs.com/jessyswing/p/10589300.html
總結
以上是生活随笔為你收集整理的OO第一单元总结博客的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: git 错误 RPC
- 下一篇: 字节流的 创建 写入文字
