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) 直覺上告訴我們需要輸入三個變數,就可以接受。

註解

  • 盡量用程式碼本身表達意圖,因為改程式碼後不一定會記得維護註解,到時候註解沒有表達真正程式碼的功能就會誤導。
  • 一些不太會變的東西是可以放進註解的,例如在程式碼前面放上數學公式的意圖與相對應的變數名稱,或是一些先備知識。
  • 千萬不要留下被註解的程式碼,時間久了會不知道為什麼被註解起來,很多版本管理程式可以幫你存下舊的程式碼,所以在當下版本要將不被使用的程式碼刪掉。

編排

  • 將不同概念用空白行分隔,降低垂直密度。就像文章需要分段,從頭到尾只有一段的文章看起來會很痛苦。
  • 一行程式碼最好不要長到需要橫向捲動螢幕,若有一堆變數要換行排列整齊。
  • 要縮排表示流程階層,例如 if 或 while,這樣比較好理解流程的相互關係。

看了上面的方法,會發現簡潔程式碼的原則都很基本,但小小的改變累積起來會有很大的不同,在回頭看舊程式碼後就可以更快的了解它的意圖,也才能更快的開始著手寫新的程式碼。

參考書目:
無瑕的程式碼:敏捷軟體開發技巧守則
重構─改善既有程式的設計

 


在〈“Clean Code 簡單原則”〉中有 2 則留言