OO第一階段紀實
$ 0 寫在前面
在DDL一次次的推動下,歷經三個周期的更叠,一個月的時光匆匆而過。謹撰此博文,以記錄這一段見證成長的心路歷程。
$ 0-0 JAVA“一天速成”
沒有修習過傳說中的“OO先導課”,在學期開始之前也從未接觸過JAVA編程,真正的從零開始學JAVA。有了先前課程的基礎,經過了一年的積累,在短時間內學會使用一門語言並不困難,在加上JAVA天然的與C語言之間的某種相似性,也使得“JAVA一天速成”並非遙不可及。
$ 0-1 “過程式”VS“對象式”
但值得註意的是,JAVA作為一種面向對象的開發語言,其本質上與C語言有著重大差異,只不過在第一次作業的時候,由於對JAVA的一無所知,就也只能按照頭腦中既定的那種根深蒂固的過程式程序模式來編寫。而作業要求“註意體會過程式和面向對象式程序的區別”也成為了一直困擾我的難題。
$ 0-2 棘手的需求
在第二次作業中,由於有了第一次作業和實驗課的經驗,我也閱讀了更多身邊同學所編寫的代碼,使我對面向對象的概念有了進一步的了解。同時由於作業項目引入了一個具體的應用背景,也使我對“類”的定義和使用更加得心應手。不過新的難點總是會不斷湧現,這一次我所面臨的新難題便是—指導書。可以說指導書就是一種用戶需求,相比於在未來工作中可能遇到的需求而言,指導書可以說是將規定做到盡量細致了。但是大量的內容同時湧現,個別定義的模糊不清,成為了對我的新的考驗。
$ 0-3 慘痛的教訓
緊接著第三個工作周期,也就是剛剛結束的那項任務對我而言是一次慘痛的教訓。由於第三次作業的內容繼承自上次作業,同時對上次作業的內容有了更進一步的需求,這就考驗了我們所設計的代碼的可擴展性。另外捎帶請求、同質請求獨特的判定方式相互交織,也為調度方法的設計與實現增添了不小的難度。能否做好與上次作業的銜接、能否架構好頂層設計便成為了這次作業的主題。
$ 1 第一次作業
$ 1-0 從I/O開始
萬事開頭難,一個程序往往是要以一組輸入作為起始,由於一上手還不了解“對象”的概念與使用,java的一組用於控制輸入的語句在我看來顯得“玄妙異常”。
import java.util.Scanner; Scanner input = new Scanner(System.in); if(input.hasNextLine()) { text = input.nextLine(); } input.close();
這裏的input為實例化的一個java內置Scanner類裏的對象,System.in作為Scanner構造方法的參數被傳遞進去,hasNextLine()和nextLine()都是Scanner類裏的方法。最後的close()方法的調用也是編譯器的一種建議,不寫該方法會收到一條警告信息。
註意在讀入一行前,一定要先判斷是否存在下一行,從而避免直接落入Exception的處理之中。同時也盡量在讀入階段末尾調用close()方法,以免出現未知錯誤。
下面列出一些常用的Scanner類中的方法,便於日後使用時查閱。
(圖片來源於網絡資料)
$ 1-1 正則!正則!
在上一學期的《計算機組成原理》Project 0中我們了解到,正則表達式是一種有效的字符串匹配機制,其本質就是基於FSM實現。因而這裏我也嘗試了一下在java中用簡潔的正則式來取代原來書寫FSM所采用的繁瑣的狀態轉移。
正則式的表達雖然簡潔,但隨之而來的問題也著實為我帶來不小的困擾—爆棧。
由於本次作業的輸入是有一定規模的,總共輸入項數的上限多達50*20。對於這種規模的輸入,如果不加以拆分,直接進行整體正則匹配將無法通過壓力測試(實驗證實)。
由此我便得到啟示:在待匹配的字符串規模較大時,可以采用以下語句,來實現對字符串的拆分:
// while(m.find()) // m.group(0)
其中find()方法用於判定找到了帶匹配的字符串,group()方法則可以用於截取出匹配到的字符串。
至此,正則式的兩大用途都很清晰了:一是可以用於格式檢查,二是用於從原字符串中截取符合規格的標準段。那麽如何綜合應用這兩種功能,去解決對大規模輸入的匹配呢?
我在第一次作業中就應用了分段匹配的辦法,每次只匹配一個多項式的長度,並將匹配好的部分存入一個String中,逐次累加,最後比對存儲的串是否和原串相同,即可完成匹配。
還有幾點值得說明的是,在正則式中盡量應用^$來標記待匹配字符串的首尾,用非捕獲性的(?:)來代替捕獲性的()匹配。這兩處細節可以優化我們的正則處理過程。
$ 1-2 防禦與攻擊
第一次在互測階段被發現了一處bug,這處bug出現的原因在於我對“-0”以及“前導0”的理解不夠全面。僅考慮了形如“-0”的情況,卻忽略了“-0”也可能存在前導零。這就在被測試階段,給測試者留下了可攻擊的漏洞。
在進行測試時,我所遵循的邏輯也是由表及裏的策略:即通讀指導書、略讀I/O、檢查異常處理、深入理解代碼邏輯幾個步驟。
拿到的第一份代碼的漏洞出現在缺乏try-catch(關於try-catch的重要性將在下節敘述)。在這個基礎上,在合理範圍內使程序crash的幾率就大大增加。緊接著我發現,作者運用了大量的定長數組代替長度可變的數組,而且在Input階段的正則匹配也采用了十分開放的貪婪匹配策略,這就為程序引入了潛在威脅。
因此,通過構造一組恰好數組越界的小型數據輸入用例便crash掉了這個程序。
$ 1-3 try everything && catch me if I fall
任何情況下程序都不應crash。
這一要求一點都不過分,對程序魯棒性的追求沒有極限。讓自己的程序在高強度的測試下保持穩定性,是每位設計者必須要致力於的目標。
因此,永遠不要忘記try和catch!
此外,結合後續作業中的經驗,try語句塊不僅應出現在最頂層,在輸入階段、類型轉換階段、訪問定長數組階段等“危險”時期,最好都應用try-catch結構,而且為了忽略掉異常情況使程序繼續正常運轉下去,在內部的try-catch結構可以不用exit(0)。
$ 1-4 代碼度量分析
$ 2 第二、第三次作業
$ 2-0 “面向對象”的進一步理解
這一次作業由於有了具體的應用場景,在加上設計建議,讓我有機會進一步理解了“面向對象”的概念。
為了便於我自己的理解,我認為我們在JAVA中定義的class視作和C中定義的struct類似,都是一類封裝好的屬性的集合,但class在struct的基礎上還能在其中定義方法。盡管這種理解並不準確,但確實在我編程的過程中給我了一些提示。
“對象”則是我們實例化的一個具體的編程實體,實例一個對象的好處是其中的數據易於維護。這就和我之前在編程時喜歡用的全局變量截然不同,對象內部的數據易於保護,對外界不可直接訪問與修改,這就大大降低了由數據共享所引入的風險。
$ 2-1 多變的需求與應對策略
第二、第三次的指導書內容龐大,issue的討論區也有很大的信息量。如何整理出其中的邏輯,從何處下手是工作的重點。和測試同學交流過後,我也很認同他所提到的前期設計。而我也不得不承認,由於設計工作不夠充分,導致了很多的細節考慮不夠周到,也為我後來的debug工作造成了重大的影響。
一種值得借鑒的好習慣是,把指導書和issue中提及的關鍵點提取出來,記錄到文檔中,便於設計時查看;采取顯式的設計模式,將設計流程完整的呈現出來,對自己的設計也是一個提示作用;測試驅動開發,測試點的設計要用心,多考慮邊界情況,不斷校正模型。
$ 2-2 新一輪的防禦與攻擊
在這一個周期的攻防階段中,由於最後的提交階段時間緊迫,沒有來得及提交readme、還有很多細節的地方,比如Interface/toString()、包括一個已知的尚未來得及調試的bug。
在同質請求和捎帶請求混雜在一起的情況出現時,邊界條件的判斷就顯得尤為重要,尤其是同質判斷和可捎帶判斷邊界差了1秒的開關門時間。所以邊界時間的+-1、是否取等號都需要納入考慮的範疇。
由於設計工作的欠缺,後期陷入了調試的深淵。最終也因為一個邊界取值的原因,被測試人員發現了一處bug。
同時測試中也發現了一些重要的bug,在類型轉換過程中,溢出了數據類型規定的範圍,導致程序異常終止。這也就是我在$ 1-3 中提到的,try-catch結構內置的意義,同時也要為輸入的正則匹配加以限制,盡量避免貪婪匹配的使用。
$ 2-3 代碼度量分析 && 類圖
$ 2-3-0 第二次作業
$ 2-3-1 第三次作業
綜合$ 1-4和本節的結果分析,從度量分析結果來看,代碼的邏輯復雜度、嵌套塊深度等指標在三次作業中均超出了標準值。內聚度不高、耦合度超標也是在設計中凸顯的矛盾。在代碼邏輯的部分、方法的抽象度不高,很多功能相似或相近的方法代碼段出現大量重復。在第三次作業中這種矛盾更為明顯:Complexity 均值高達6.3;Parameters per method 均值達1.3;Block depth 也超過標準值。這些顯然都是由於代碼中大量的循環嵌套、條件分支的交互錯雜造成的,這也為我的調試帶來了難以估量的災難性後果。
工程化方法的指導思想在於“高內聚、低耦合”,但要做到這一點顯然需要前期大量的準備工作,頂層的架構、顯式的設計過程、測試驅動的開發,都將有效幫助我在下次的設計中進一步優化。
$ 3 關於Object Oriented 的一些其他感想
$ 3-0 關於申訴
第一次OO作業的申訴經歷,足以證明:我航學子的學品與素養,托得住,信得過。即使在OO的高壓雙盲互測體制下。依然可以保持人與人之間最單純的信任,真心希望能與諸位同行一道,摒棄流言蜚語,摒棄偏見,以此課程平臺為契機,增進交流,建立信任。學在其中,樂在其中。共同收獲技術上的不斷提升!致力於正能量的傳遞,我們一起!
$ 3-1 關於無效作業
在OO Project2 中,我遺憾的看到了身邊有很多朋友被申報了無效作業。而產生無效的原因也很令人詫異。看到查找別人個人信息的方式也不禁令我大跌眼鏡。故意找別人的個人信息申無效,難道這是課程組設計的本意麽?交流技術,看代碼,看文檔就好。一般情況下難道會有人特意點開pdf屬性看作者?或是用強行打開.project文件?就為了找個人信息申無效?為了多得分方法有很多,最好的方法還是做好自己的任務,畢竟每個人都很努力,還是希望能夠彼此多尊重對方的勞動成果吧。
$ 4 特別致謝
“沒有人是一座孤島”。說實話,前三次作業的過程充滿了艱辛,一路走來,必須要感謝在這一過程中給我最大幫助的朋友們。他們是:
中國好室友LCJ,對於我解決各種瑣碎的疑難總是回應以最大的熱情,尤其是第三次作業ddl前極限操作時幫我趕制readme和interface,讓我至今回想起來都很感動。
RHX哥、ZZH哥,給我在解決問題上提供了大量寶貴的思路。NYZ、GZP在具體的技術實現的細節上給了我大量的指導。ZKN、LHY、LQY為我提供的大量測試用例,幫助我有效的檢驗修正模型。JSH搭建了公共測試平臺並維護,造福全系同胞。
當然更要感謝在每次互測階段,秉持公平公正、認真互測的測試者,和堅持積極溝通交流並給我充分理解的被測試者們,是你們讓我看到的跟多的是這一機制的積極一面。
希望在緊接著即將到來的挑戰中,總結經驗、繼續努力,不斷提高技術能力。也希望能盡我個人之所能,為這一課程做出一份貢獻!
OO第一階段紀實