面向對象第一單元訓練總結

1、前言

  "面向對象編程語言的問題在於,它老是附帶着全部它須要的隱含環境。你想要一個香蕉,但獲得的倒是一個大猩猩拿着香蕉,而其還有整個叢林。" 正則表達式

— Joe Armstrong(Erlang語言發明人) 算法

  在許多個夜晚,當我面對着Eclipse複雜的界面,仔細思索本身前幾天剛剛寫下來的、如今卻已經不知道是什麼意思的代碼的時候,我經常會質問本身:若是沒有了IDE,你還能寫得下去代碼嗎?使人遺憾的是,直到目前爲止,不管我何時問本身這個問題,獲得的答案都是否認的——當我爲了添加一個新的功能,向一個類增添一個屬性或方法的時候,連帶着就須要向其它類添加其它的屬性或方法,從而爲這個類提供它所須要的數據;而爲了提供這些數據,這些類又須要向另外的類申請更多的數據,直到全部的類混在一塊兒,成爲一灘稀泥,嚇跑全部試圖去維護它的人。最終的結果就是,我本身也忘記了個人類裏面有哪些數據和方法,那些用於核心算法的方法和用於向其它類提供數據的方法互相交纏在一塊兒,把整個類撐成了一個畸形的胖子。我只好將本身的顯示器豎過來擺放,以便本身能夠在一頁裏完整地看完一個類的定義——這仍是在個人顯示器長寬比是21:9的前提下。然而即使如此,若是沒有了自動補全,我將依然步履維艱。編程

  而調試的過程則更加絕望。有兩個類,它們彷彿要比賽似的一個比一個長,卻你中有我我中有你,每修改一處微小的地方,就會引起一連串的連鎖反應,致使Bug從莫名其妙的地方冒出來。而在試圖去修復這些剛冒出來的Bug時,又會引起新的Bug。不借助於IDE的單步調試功能,我甚至沒法肯定在本身的代碼中,Bug可能會出如今哪個方法中——由於每個方法都與另外的方法產生了強耦合,引起一個方法中Bug的代碼極可能在另外一個方法中。即便我幸運地調好了一個Bug,我依然不敢確定本身的程序可以經過以前已經測試過的樣例,因而我只好把以前的樣例從新再跑一遍,並祈禱它不要出錯。這樣一連串的過程下來,再多的時間也不夠用。數據結構

  本次總結,正是爲了解決以上的兩個問題。編程語言

2、三次做業的程序結構分析

一、第一次做業 – 多項式加減

  設計回顧:爲了不正則表達式爆棧,解析多項式階段採用了雙層解析的方法,先解析外層花括號,在解析內層小括號,有效避免了正則表達式效率低的缺陷。在總體的設計上,解析模塊與計算模塊分離,計算模塊一旦得到一個多項式,就必定得到的是正確的多項式,減小了輸入和計算之間的耦合。計算時則採起一了一個相對簡單的算法,先對每一個多項式按冪次升序排序,而後再依次歸併。函數

  自我評價:多是因爲以前在學習Python的時候有了一點點面向對象的思惟基礎,第一次做業寫起來,我大致上遵循了面向對象的程序設計思路,而不是像C語言那樣把全部東西都堆在主函數裏。此次做業進行得比較順利,在公測和互測中都有着不錯的成績。學習

二、第二次做業 – 單電梯傻瓜調度

  設計回顧:沿襲第一次做業的設計,解析部分與計算部分合理地分離,爲核心算法的實現減小了難度。因爲採用了傻瓜調度,調度器類的代碼比較少,其核心方法爲Schedule方法,每一次調用該方法,該方法從請求隊列中選擇下一個要執行的請求,並餵給電梯類去執行。電梯類承擔了主要的狀態保存工做。測試

  自我評價:第二次做業的難度相比第一次做業有了一個難度上的提升,這主要是由算法帶來的。第一次做業可謂根本不須要算法,但對於電梯調度問題,即便是傻瓜調度,如何判斷同質請求也是一個算法上的難點。在此次做業中,我採用了一個比較簡潔的算法來判斷請求是否同質,時間複雜度比較低,代碼實現也很簡單。但萬萬沒想到,這個看似簡潔的算法爲第三次做業挖了一個大坑。設計

三、第三次做業 – 單電梯ALS調度

  設計回顧:這一次做業相比第二次做業,重寫了調度器類的Schedul方法,並大幅調整了電梯類的代碼結構。Schedule方法依舊保持着其優秀的特性:每次調用,從請求隊列中返回下一個將被執行請求。但爲了保持這個特性,調度器類的代碼變的冗長,而且電梯類增長了許多過後看來多餘的方法,從總體上來講,這個設計相比第二次做業的設計,顯得異常繁雜而醜陋。3d

  我的評價:第三次做業的算法難度相比第二次做業又提升了一個層次。在此次做業中,想要又快又好地寫完,就必須有一個優秀的算法。然而我在寫此次做業的過程當中,發現第二次做業的算法沒法很簡單地移植到第三次做業上來,又限於繼承Scheduler類的要求不能全盤顛覆上一次的代碼,所以只能在現有的基礎上不斷打補丁來完成需求,形成了我在前言中遇到的那兩個問題。第三次做業整體上來講是失敗的,程序主算法的時間複雜度達到了平方級別,而且代碼又臭又長,難於調試和修改。若是有機會的話,重構應該是一個必選選項。

3、本身程序中存在的Bug

  這三次做業在測試環節整體來講仍是比較幸運的,只在第三次做業被別人挑出了一個涉及到100條輸入邊界判斷的小Bug,其它的地方都沒有被查出問題。固然,這個Bug是確實存在的,對此我也無可反駁。我分析了一下這個Bug出現的緣由——我在對輸入的請求進行是否合法的判斷時,是分兩個步驟進行的,第一個步驟是正則表達式的過濾,第二個步驟是判斷特殊狀況(一層向下或十層向上),其中任何一個步驟不經過都會當即報錯,但只有第二個步驟纔會檢驗是否超過100條指令。這意味着個人輸入判斷流程應該加以改變:把這兩個步驟合併成一個步驟,並進行統一的報錯和邊界判斷。

從程序結構上來說,將輸入的合法性判斷放在兩個類裏確實不是一個好的設計。在面向對象的程序設計中,一個類應該一次性作完它所應作的工做,並把正確的結果傳遞給其它的類。這個Bug歸根結底,仍是設計上的欠缺和疏漏。

4、如何發現別人的Bug

  我不太願意去摳別人邊角上的細節,所以個人測試主要集中在功能性方面,有時可能會稍微測一點Readme相關的東西,但毫不會作出跟別人玩文字遊戲這種缺德  事。與其絞盡腦汁去噁心別人,不如用這時間去努力提升本身的編程水平。

  拿到測試任務後,首先把本身構造的、用於測本身程序的測試用例在別人的程序上跑一遍。若是前期構造測試用例上花了功夫,這個時候應該就已經能測出絕大多數的Bug了。若是沒有的話,再將程序下載下來,並經過IDE的單步調試功能分析對方代碼的行爲,尋找對方代碼中的邏輯漏洞,並針對性地根據這些漏洞設計測試樣例。最後,查看Readme,對邊界條件和特殊狀況進行檢查。這一套流程作完以後,若是找不到Bug,那就意味着基本沒有Bug了。

5、心得與體會

  在前言中我提到,本次總結的最終目標在於解決類之間耦合性過強致使的代碼冗長、調試不易的問題。通過對以前三次做業代碼的度量和分析,我有了瞭如下這個即將支配我從此的做業的心得和體會——

  把算法從戰略需求變成戰術需求。

  這聽起來很玄,但說透了卻很簡單。在以往的C語言程序中,程序=算法+數據結構的鐵律支配着每一次做業的設計和實現,算法是整個程序的戰略需求。所以,C語言代碼的編寫過程當中,一切都是爲算法服務的——執行核心算法的函數能夠從任何地方得到它須要的任何數據,它的觸角伸到程序的每個地方,像一個全知全能的神。這使得在C語言中構造一個大型的算法很是簡單,但也致使了用C語言寫出來的程序稍一不注意就會有很是強的耦合性。

  在Java中,類的封裝特性使得在不一樣的方法(這裏特指不一樣類中的不一樣方法)之間傳遞參數變得比較困難。所以,若是再沿襲C語言的設計思路,把算法放在戰略的層面,一切數據和方法都爲算法服務,勢必會致使每個類都互相糾纏在一塊兒,而類屬性的Private可見性會使它變得更加複雜。能夠說,在Java中,一個算法不適合橫跨多個類而存在,若是硬要把算法做爲整個程序的戰略目標,讓一個類去承擔所有的核心算法,就不太可能避免類之間的強耦合。

  然而算法又是如此的重要,毫不可能忽視它的存在。對此,個人解決辦法是,把算法在整個程序中的重要性作一個降級——從戰略層面降到戰術層面,儘可能確保一個算法只須要類內部的數據就能夠實現。這就須要在一開始對程序各個類作出一個良好而完整的規劃。在Java程序設計中,頂層規劃取代了原來的頂層算法,一個大的、戰略層面上的算法被拆成幾個小的、戰術層面上的算法,交給各自所屬的類去實現。經過類的封裝,把算法禁錮在一個小的範圍裏,不讓它去調用其它類中的數據。這樣,就能夠很好地避免類之間數據的耦合,也就能最終解決我遇到的難題。

  總而言之,用Java編寫程序的時候,思路和以前的C語言能夠說是徹底不一樣的。C語言能夠馬上開始寫起,不須要過多的構思,由於全部的數據都是唾手可得的,一個函數想要獲取數據不存在任何難度。但在Java裏,私有屬性大大增長了其它的類訪問本類數據的難度,爲了防止後期不斷增增補補,一開始就要對程序做出一個總體的構思和規劃。若是規劃合適,那寫出一手漂亮的代碼,不過是動動手指的事。

相關文章
相關標籤/搜索