重構代碼 01

1.介紹

傳統的觀念是先設計,然後進行寫代碼。但是最初的設計往往都並不是完美和盡然的,重構顯得非常必要。重構所強調的就是在寫代碼的過程中再不斷地進行設計和修改,從而達到最終的設計,即使最初的設計很混亂也沒有關係。有可能一小段代碼本身沒什麼可改的,但是如果是在一個大項目中的話,就有改的必要了。

首先,你的方法代碼不能太長了,一旦代碼太長了,系統要進行修改的時候就很難修改,因爲到時候你也不知道該改哪裏。還有,太長的代碼往往很難重新使用,因爲涵蓋的邏輯太多,涉及的類太多,一改通常就基本上全部要修改。記住以前聽說的一個規則,方法不要超過15行。千萬不要使用代碼拷貝,因爲你一旦拷貝了代碼,遇到要修改,你需要改的地方就增加了,出錯的機率也就增加了。

當你寫了一段代碼之後,最好就進行一個測試,因爲是人總會犯錯誤。測試完了再進行下一步,這樣才能保證代碼的可靠性,也少了後期的煩惱。測試代碼要做到讓代碼自己進行檢查,而不是人爲地去進行,這樣會花費很多時間,而且也容易出錯。千萬別覺得寫測試浪費時間,這個是值得花時間的,謹記這一點。在進行代碼重構之前,記住先要確保你有一系列的test,儘量想一想可能發生的各種情況,而且這些test都必須是self-checking的。

定義變量,參數和方法的時候,對其名字進行深思熟慮,這樣可以讓你的代碼邏輯更加清晰,要做到別人能讀懂你的代碼。

儘可能地去掉臨時變量,通常,如果有方法可以獲取臨時變量的話,就去掉它。這種情況往往是和循環有關,這個時候你原先的一個循環如果拆成多個方法就可能變成了多個循環。需要考慮一個問題,那就是原先的一個循環肯定大於分拆後的任何一個循環的時間,如果分拆的循環不一定使用的話,多個還是要划算一些。還有就是,即使是方法的話,只要是在這個類中,那麼大家都可以調用,更別說public方法了。儘量避免多重循環。

如果你要通過對某個屬性判斷而得出一定的結論,那麼最好將該方法寫在那個屬性所在的類中。


2.什麼叫重構

爲了讓軟件更加容易理解並且不用修改其可見的功能就能使得維護更加容易,從而對軟件的內部結構進行的更改。

 可能需要長達幾個小時的時間進行整個的重構,在這個過程中你需要不斷地進行小範圍的重構。

需要尤其值得注意的是: 軟件的接口功能絕對不能改。

一句話,就是重構其實就是引入了更多的間接性到程序中。將大的儘可能的變小,雖然變多了,但是也就更加靈活了。但是,它同時也是雙刃劍,東西變得太多了以後,也很難進行維護,可讀性也許也隨之變得很差。所以,一句話,在保證塊的數量最少的前提下,放手去將大的變小。


3.爲什麼要進行重構

如果代碼設計得很爛的話,做同樣的事情往往要多寫很多代碼。很大的原因就是很多地方都有同樣功能的代碼。改善設計的一個重要的方面就是儘可能地去掉重複的代碼。但是記住,減少代碼的數量(功能相同的話),基本上並不會使得代碼整體運行速度大幅提升。但是,代碼量減少了以後,對於以後的修改大有好處,因爲如果重複代碼少的話(假設理想狀況下完全沒有重複代碼,任何東西出錯了都只需要改一次),需要修改的地方就少了。如果消除了重複代碼,那麼你就可以保證任何一段代碼都只表達一次,並且一次就說清楚了。

還有一個非常重要的原因就是,你的代碼往往是需要給別人看和修改的。如果別人能更好地理解你的代碼,那麼進行修改,添加就更加迅速。其實電腦本身根本無所謂,多編譯一會兒並不影響任何性能。重構可以讓代碼更具有可讀性,這樣的話,別人使用起來更加方便,整體的工作效率也就提高了。

儘可能少的去專門記代碼,如果你在看你自己的代碼的時候,感覺有些理解不能,這個時候可能就要考慮重構了,持續這樣進行,你自己就不用專門去記自己寫的代碼,而只是進行簡單的查找就能知道代碼的用途,邏輯等等。當然,用久了自己就能記住。更加重要的是,一旦你把一些邏輯理得更加清楚以後,你對編程本身認識的高度會不斷地上升。

還有,可以幫助你發現bugs。一旦邏輯清晰了,一些問題也就自然而然地暴露出來了。

最後,所有之前說的,都是爲了達到這樣一個目標: 使得你編程的速度更快。按理說,找重複代碼,重構,寫測試和運行測試都會降低編程效率,但是爲什麼會提高效率呢?其實,還是那樣說,和寫unit test一樣,寫的時候當然覺得浪費時間,沒事也就算了,但是一旦要是遇上有事了,多餘的時間就搭進去了。代碼要是設計得好,從長遠來看,肯定是 節約時間的。代碼設計得差的話,你要花時間去理解代碼,而且有些代碼無法進行重用,你又得重新寫,時間被就耽誤了。


4.什麼時候進行重構

其實並不需要專門抽出時間,然後說我要專門花2天的時間來重構代碼,重構應該是時時刻刻在發生的。並不是說,你要計劃重構,而是說你想做點其它的事情,重構剛好可以幫助你做起來更加方便。記住3條規則:第一條,當你想做一件事情的時候,你儘管放手去做就是了; 第二條,當你再次做類似的事情的時候,沒關係,繼續做,但是你必須警惕,告訴自己這個東西已經寫了2遍了; 第三條,如果第三次出現,那你就該重構了。

當你添加方法的時候,往往是重構最頻繁發生的時候。在已經寫成型的代碼中添加方法,往往這個方法的功能的一部分已經在別處實現了。或者換句話說,就是你要添加的方法可能對你原先的代碼有用。只要你一旦覺得重構可以讓代碼更容易被理解,馬上就動手。

還有一個重構的動機,就是我想加一個東西,比如一個方法,但是發現非常不好加。那麼就要考慮重構了,這樣將來再想添加東西的時候,也要方便一些。

回顧代碼的時候(首先必須說明,這是一個非常好的團隊習慣),可以進行重構。大家都可以貢獻自己的idea,畢竟每個人都有獨到的見解。同時,有經驗的程序員可以將自己的思路傳達給欠缺經驗的同志們。

記住,今天能做的事情今天做完了,但是明天要做的事情做不完的話,還是失敗的。所以總的來說做事情要考慮將來,在有可能的情況下,注意代碼質量,而不是盲目的按照schedule來。manager當然希望你以最快的方式來完成任務,記住,重構確實能讓你做事的效率提高,從而最快地完成任務。


5.重構可能帶來的問題

很難說,但是Database就是一個問題多發領域。往往數據庫都很難改。

還有就是Interface,接口一旦改了,相關的類,還有用戶的接口都會變,不小心會造成很多問題。如果是自己的代碼,比如你改了一個interface的方法名稱,你可以去把所有調用了這個方法的地方的名字都改了就是,除了稍微麻煩一點之外沒什麼。但是,一旦你的Interface已經publish了,這就麻煩了,你改不了,因爲別人在用,你不知道誰在用。所以,當你必須要改一個接口的時候,你必須把舊的也留着,讓舊的調用新的。


6.那些代碼有問題?

a.duplicate code,像上面說過的,重複的代碼。

b.long method,也是上面說的,過於長的方法,記住15行原則。雖然未必都必須卡在15行這個標準,但是提醒自己不要太長。

c.large class,當一個類想要做太多事情的時候,就會出現太多的成員變量。一旦一個類有太多的成員變量,那麼出現重複代碼就不遠了。如果太多的話,最好就是將相關的成員變量提取出來,重新寫一個新的類,避免一個類裏面太多東西。搞清楚client想要怎樣使用這些類和接口,然後決定怎樣進行細分。

d.long parameter list,嘗試傳遞一個對象作爲參數比傳遞一長串的參數要好得多。要是傳對象,需要什麼就拿什麼,不需要的就可以不管。要是傳參數,需求增加或者減少,就會一直加加減減,很麻煩。

e.divergent change,如果你覺得你只要一改某個東西就會引起某一個類的3,4處改動,那麼最好有2個對象。

f.shotgun surgery,一個改變會引起很多類的改變,那麼最好將這些改變都放在一個類中。move method,move field

g.feature envy,意思就是,一個類的方法往往喜歡用到的不是自己所在類的變量,反而是其它類的變量,如果這種情況總是出現的話,就把這個方move到那個類中去。

h.data clumps,就是說應該避免成員變量的list和參數的list。

i.primitive obsession,不太明白

j.switch statements,凡是看到switch語句,就應該立刻想到多態,也不太清楚

還有很多這樣的名詞,反正都是各種覺得代碼可能不合理的地方。但是,個人認爲,這個真還得看經驗,也得看代碼的實際情況。有一點關於comments的,就是說是這樣的,寫comments本身是非常好的,但是用辯證的觀點來看就是,你的代碼寫得越是清晰,需要的comments往往就越少,所以說當你發現你不得不依賴comments來讓別人讀懂你的代碼的時候,你就要考慮重構了,到最後最理想的結果就是comments反而變得有點多餘了。


重構的時候力求進行最小的改動然後立刻進行測試,通過以後再進行下一步。如果你的改動稍微大並且出錯了,那麼返回原來的代碼,然後選擇比較小的範圍進行改動。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章