淺談重構

前篇 Clean Code 簡單原則 中,談到寫出好的程式碼有兩個方法:一個是一開始就寫出乾淨的程式碼,但在對整個程式架構都不明瞭的情況下,就得寫出完美的程式碼非常困難;另一個則是靠重構程式碼,透過一次次等價交換小塊程式碼,讓其慢慢變得更乾淨。 重構是什麼? 重構就是一種在不改變程式外在表現的前提下,將其程式碼變得更容易閱讀,且更容易修改。 通常聽到重構,可能會聯想到大興土木的變革,其實不然。重構是一種寫程式的小習慣,是每次動手寫新程式前都會做的事情,有點像是打掃房間一樣,每次進房看到有髒亂的地方就順手整理一下,維持房間的乾淨整齊,而不是等到東西都堆滿地,想要放一張新的桌子都沒地方可以擺。 另外,重構可以幫助我們慢慢形塑整個系統,讓軟體一直貼近實際需求。以往瀑布式開發一開始精心規劃的架構,拆成一塊一塊後分批完成,最後統合起來形成整個系統。但需求一直在改變,從規劃到釋出軟體經過了一年以上,到時市場趨勢已經不是當初規劃的那個樣子,會造成極大的風險。 ![1399244627-1379743011](https://jimmylab-images.seisblue.com/uploads/2018/10/1399244627-1379743011.jpg) 隨著敏捷開發的出現,人們開始縮短開發週期,加快取得使用者的回饋,以利調整方向,但這會造成程式碼必須頻繁的修改。 ![1486430757-1509367922_n](https://jimmylab-images.seisblue.com/uploads/2018/10/1486430757-1509367922_n.jpg) 改動就會有風險,在開發的過程中需要利用**兩頂帽子**的設計方式:「重構」與「添加新功能」。重構確保所有功能不被更動的前提下,利用每次的等價交換,重新思考如何將程式碼分拆成小塊容易維護的樣子,並將區塊間的相依性降低,之後再來添加新功能。 講了這麼多,重構到底要怎麼進行呢?先有程式碼好壞的概念後,找到程式碼中不好的地方,用查表的方式替換成新的形式,代換完後測試一下程式,觀察結果是否與更改前一致,若一致就可以再尋找其他地方進行重構或添加新功能。 在 重構─改善既有程式的設計 這本書提到一些程式碼的壞氣味: Duplicated code 重複的程式碼 Long method 過長函式 Large class 過大類別 Long parameter list 過長參數列 Divergent change 發散式變化 Shotgun surgery 散彈式修改 Feature envy 依戀情結 Data clump 資料泥團 Primitive Obsession 基本型別偏執 Switch Statements 可怕的 Switch Parallel inheritance hierarchies 平行繼承體系 Lazy class 冗員類別 Speculative generality 夸夸其談未來性 Temporary field 迷惑的佔時欄位 Message chains 過度耦合訊息鏈 Middle man 中間轉手人 Inappropriate intimacy 狎暱關係 Alternative class with different interfaces 異曲同工的類別 Incomplete library class 不完善的程式庫類別 Refused bequest 被拒絕的遺贈 Comments 過多的註釋 在大話重構這本書中提供了一些較簡單的方向:...

<span title='2018-10-01 03:51:10 +0800 +0800'>October 1, 2018</span>&nbsp;·&nbsp;1 分鐘&nbsp;·&nbsp;Jimmy

Clean Code 簡單原則

在介紹 Clean Code 之前,想想為什麼會寫出糟糕的程式碼呢?可能是趕進度,也有可能只想早點下班,甚至是連好壞程式沒概念。也有人說程式會動就好了,花這麼多時間讓程式碼變整潔,不是很沒效率? 程式會動就好這件事聽起來很美好,但是需求一直在改變,今天寫好了一個程式後,使用者用了一段時間一定會想要加點新的東西進去,這時如果是糟糕的程式碼,得花非常多時間搞懂之前做了什麼,才能下手改程式碼。 有人統計過閱讀程式碼與寫新的功能所佔的時間約為 10 : 1,由此可見,原有的程式碼越難懂,就得花越多時間在理解程式的原意,開發的時間也就拖得越長。另一件頭痛的事是,如果程式碼糾結在一起,很難保證改了一段程式不會影響到其他區域,到時候改了一個錯誤,出現更多的錯誤,就會沒完沒了。 我們了解到寫出雜亂的程式碼,會導致程式越來越難維護,隨著時間的推移,程式碼產出會慢慢趨近於零,到時候不得不放棄這團糾結不清的系統。但事情往往沒這麼簡單,舊系統經過了時間的考驗,修了無數的錯誤,在重起爐灶時不太可能將所有條件考慮進去,導致新系統不堪使用。 要怎麼避免這種兩難事情發生呢?有兩個方法: 一開始就寫出整潔的程式碼,但新手往往不夠熟練能第一次就寫出乾淨的程式碼,可以先了解好程式碼的方向,慢慢培養寫程式的品味。 重構程式碼,利用代換等量的程式碼讓程式解耦,並利用測試驗證重構的正確性。這主題可以獨立成一篇:淺談重構。 這裡列出 Clean Code 書中幾個簡單的原則,剩下較難或繁瑣的觀念,例如物件、類別、錯誤處理、系統架構 … 等,可以等程式功力更熟練時再回去詳讀: 命名 要讓名稱確實表達意圖,絕對不要用 aaa, a1, a2 這種完全不知道意圖的名稱,至少讓人一眼看出變數代表的意義,例如:traceStartTime 或是 traceCount。 名稱避免誤導,像 O 跟 0,I 跟 l 跟 1,或是 dirToTheDataBase 跟 dirToTheDataManager 這種太相似的變數。 不同的變數,意圖應該要有明顯的區別,像是 fileDir 與 dataPath 實際上是指同一件事情,不應該有兩個變數做重複的事,若有這種情況必須重新思考程式架構。 名稱要可以唸得出來,在互相討論程式碼時才比較有效率,不要有 genyyyymmddhrmnss 這種無法發音的名稱,generationTimeStamp 會更好。 盡量使用大家都熟悉的名稱,課本公式上常用的代稱就可以使用,像寫極座標時,用 r, theta, phi 就是可以接受的命名。 函式 函式盡量越短越好, 超過螢幕可以一眼看完的函式,在閱讀上一定會有困難,所以函式大概控制在 20 行以內。 函式只做一件事情,如果裡面非常複雜,通常可以拆成更多的小函式。 函式內只有一層抽象概念,太細節的功能就包給更小的函式,需要統合的功能就交給更大的函式。 函式通常是做一個動作,所以用動詞開頭命名會比較適當,像 getTimeTable 或是 isDataContinuous。 函式輸入的參數越少越好,最好是零個,一個次之,兩個就不太好了,盡量避免三個以上,超過要用類別將變數包起來。但是非常直觀的變數數量,例如 plot3D(x, y, z) 直覺上告訴我們需要輸入三個變數,就可以接受。 註解...

<span title='2018-09-27 23:20:26 +0800 +0800'>September 27, 2018</span>&nbsp;·&nbsp;1 分鐘&nbsp;·&nbsp;Jimmy