網頁

搜尋此網誌

2016年5月16日 星期一

Clean Code 無瑕的程式碼

這篇是記錄「無瑕的程式碼」第 1 章至第 5 章的重點整理。

序 (Introduction)

  • 學習工藝典範 (craftsmanship) 可分成兩部分:知識 (knowledge) 和實作 (work)
  • 你必須獲得程式工藝師所知道的原則 (principles)、模式 (patterns)、實踐 (practices) 與啟發 (heuristics),並且必須研磨這些知識,透過努力實作和練習,將這些知識融入你的手指、眼睛和身體裡。

無瑕的程式碼 (Clean Code)

  • 撰寫機器能執行的細節需求稱為「撰寫程式 (programming)」,而這些明確描述的文字稱為「程式碼 (code)」。
  • 程式碼是最終被我們用來闡述需求的語言。
  • 急於讓產品上市,導致程式碼變得一團糟,當開始加入越來越多的產品功能時,程式碼就變得越來越糟,一直到再也無法管理這團混亂。劣質的程式碼可能導致一家公司的倒閉。
  • 勒布朗克法則 (LeBlanc's Law):待會兒等於永不。
  • 花時間保持程式碼的整潔,並不只關係到成本的效益,還關乎到專業職場的生存之道。
  • 錯並不在這些事物上,而是在我們本身,我們不夠專業。
  • 你製造了爛的程式並不會因此趕上截止期限,事實上,爛程式只會馬上讓你的開發速度變得更慢,並導致你錯過截止期限。在截止期限前完成工作的唯一方法,也是讓開發速度變快的唯一方法,就是隨時隨地,都確保程式碼盡可能的整齊潔淨。
  • 能夠分辨程式碼的好壞,不代表知道如何寫出 Clean Code。「程式感 (code-sense)」是能寫出 Clean Code 的關鍵因素。
  • 沒有辦法先不讀程式就先去寫程式,所以讓程式碼更容易閱讀,也會讓程式碼變得更容易撰寫。
  • 光是把程式碼寫好還不夠,程式碼還必須持續地保持整潔。

有意義的命名 (Meaningful Names)

  • 選一個好的名稱是相當花時間,但省下來的時間比花掉的時間還多。
  • 問題不在於程式碼的簡易度,而在於程式碼的隱含性 (implicity):即程式的上下文資訊未能由程式本身明確地展現出來的程度。
  • 拼字與意圖相似的字詞就是恰當資訊,而使用與意圖不一致的拼寫就是誤導了。
  • 增加數字的序列或無意義的字詞或許可以滿足編譯器的規定,但這並不是一個好的程式設計。假使名稱必須有所不同,那麼它們也應該代表著不同的意義才對。
  • 讓你的命名能夠唸得出來。如果你唸不出你取的名稱,那就只能像白癡般用拼音來討論它。
  • 長命名勝過短命名。如果一個變數或常數在程式裡不少地方都可能使用,那最好給它們一個容易被搜尋到的名稱。
  • 現代程式語言有更豐富的型別,而且編譯器或編輯器會替我們記住型別,並要求這些型別一致,因此命名進行編碼、加上額外的字首或字尾顯得沒有必要。
  • 專業的程式設計師知道「清楚明白才是王道」,專業人士運用本身的好能力,寫出讓別人可以瞭解的程式碼。
  • 類別和物件應該使用名詞或名詞片語來命名,類別的名稱也不應該是動詞,方法應該使用動詞或動詞片語。
  • 替單一抽象概念挑選一個字詞,並堅持持續地使用它。
  • 避免使用同一個字詞代表兩種不同的目的。使用同一個字詞代表兩種不一樣的想法,基本上就是雙關語,這必須避免。
  • 優先使用電腦領域的技術性名稱命名變數,其次才是使用問題領域的詞彙來命名。
  • 較短的命名若能清楚地表達涵義,通常好過於較長的名稱。盡量減少在名稱上加入不必要的上下文資訊 (context)。

函式 (Functions)

  • 關於函式的首要準則,就是要簡短。第二項準則,就是要比第一項的簡短函式還要簡短。
  • 每個函式都一清二楚,透露出本身的意圖。每個函式帶領著你至下個函式,這就是函式該有的簡短。
  • 函式應該做一件事情。它們應該把這件事做好。而且它們應該只做這件事。
  • 如果函式只做了函式名稱下「同一層抽象概念」的幾個步驟,那麼,這個函式就算是只做了一件事。
  • 我們希望程式的閱讀就像是由上而下的敘事。我們希望每個函式後面都緊接著「下一層次的抽象概念」。這是降層準則。
  • 從本質來看,switch 敘述縱是在做 N 件事情。我們無法永遠避開使用 switch 敘述,但我們能確保讓每個 switch 敘述都被深埋到較低抽象層次的類別裡,而且他永遠不會被重複使用。
  • 當每個你看到的程式,執行結果都與你想的差不多,你會察覺到你正工作在 Clean Code 之上。
  • 只要函式越簡短和越集中在做該做的事情上,就越容易替函式取個具有描述性質的名稱。
  • 函式的參數數量,最理想的是零個 (零參數函式;niladic),其次是一個 (單參數函式;monadic),再不然就是兩個 (雙參數函式;dyadic)。可以的話,盡量避免使用三個參數 (三參數函式;triadic)。如果要使用超過三個參數 (多參數函式;polyadic),必須有非常特殊的理由,否則無論無論如何都不應該如此做。
  • 參數和函式處於不同的抽象層次,而且參數強迫你去瞭解目前並不那麼重要的細節。
  • 輸出型的參數比輸入型的參數更難以理解。
  • 當一個函式看起來需要超過兩個或三個的參數時,很可能需要將當中的一些參數包裝在一個類別裡。
  • 在單一參數的形式中,函式與參數要形成一個動詞/名詞的良好配對。另外可以使用關鍵字形式的函式命名,將參數的名稱編碼加入到函式名稱裡。
  • 副作用 (side effects) 就像是個謊言。你的函式保證只做一件事,卻暗地裡偷偷做了七他事情。函式必須要無副作用!
  • 函式應該要能做某件事,或能回答某個問題,但兩者不該同時發生。
  • 使用例外處理取代回傳錯誤碼。
  • 函式應該只做一件事,而錯誤處理就是一件事。所以,一個處理錯誤的函式,應該不能再做其他的事。
  • 不要重複自己 (Don't repeat yourself),重複程式碼也許是軟體裡所有邪惡的根源。從副程式發明以來,軟體發展領域的所有創新,都是為了消除原始碼中的重複。
  • 每個系統都是由某個特定領域的語言設計而成的,而這種特定領域語言則是「程式設計師為了描述系統所設計的」。函式是這個語言裡的動詞,類別則是這個語言裡的名詞。
  • 程式設計大師在撰寫程式時,並不認為自己是在寫程式,而是在說故事。

註解 (Comments)

  • 事實上,在最好的情況下,註解也只不過是一種必要之惡。
  • 一個註解存在的時間越久,事實就越來越偏離當初的程式碼解釋,甚至可能完全就是個誤導。原因很簡單,因為程式設計師並沒有如實地維護它們。
  • 真相永遠只存在於一個地方:程式碼。只有程式碼能忠實地告訴你它的作用,它是唯一準確的事實來源。
  • 整潔具有表達力又極少使用註解的程式碼,遠優於雜亂複雜又滿是註解的程式碼。與其花時間寫註解來解釋你所造成的混亂,不如花時間去整理那堆混亂的程式碼。
  • 真正有益的註解,是你想辦法不寫它的註解。
  • 在每個原始碼檔案的開頭寫入著作權聲明及作者資訊,就是必須且合理的註解。
  • 如果你決定要寫下註解,你就應該花上必要的時間,確保你寫出來的是最好的註解。
  • 如果有一條規則是這麼說的,每個函式都必須有一個 Javadoc ,或每個變數都應該要有註解來說明,那麼這樣的規定就有夠傻的了。
  • 想儲存修改程式碼的日誌、程式碼的出處及署名、不使用的程式碼,原始碼管控系統會是一個比較好的選擇,不要使用註解來儲存這些資訊。
  • 被註解掉的程式碼,就像是一瓶壞掉的葡萄酒,在瓶底所沈澱的殘渣,直接刪掉這些程式碼吧。
  • 註解的目的在於說明無法表達本意的程式碼。如果連註解本身都需要額外的解釋,那真是一件遺憾的事。

編排 (Formatting)

  • 程式的編排是一種溝通方式,而溝通是專業開發者的首要之務。
  • 程式碼的風格和可讀性,會在程式中立下先例,持續地影響程式的可維護性及可擴充性,就算程式碼已經被修改到無法認出原本的面貌,或者程式碼已經消失,你的程式風格和紀律依然會存在。
  • 一個原始碼檔案應該多大才好?簡短的程式碼往往比大型的程式碼更容易讓人理解。
  • 每一行程式碼都代表一個表達式或某個程式子句,還有每一段程式碼都代表一個完整的思緒。應該用空白行來分隔這些思緒。
  • 除非你有一個很好的理由,否則相近的概念不該被分散在不同的檔案裡。
  • 變數的宣告應該盡可能靠近變數被使用的地方。
  • 實體變數應該被宣告在一個大家都熟悉的地方:類別的最下方,或是類別的最上方。
  • 呼叫敘述應該要在被呼叫函式的上方,這能使得程式本身有自然的順序。
  • 程式裡的某些程式碼,希望能和其他程式碼盡可能地相近,因為它們在概念上有著相似的性質。當這個相似性越高時,它們之間的垂直距離就應該越短越好。
  • 我們希望函式呼叫呈現一種向下的相依性。也就是說,一個被呼叫的函式,應該要出現在「執行呼叫的函式」的下方。這也產生了一個良好的項下流程,由上往下查看原始碼時,可以依序發現高層模組,再接著找到低層模組。
  • 一行的程式碼應該有多寬呢?不需要使用捲軸捲到右方才是適當的寬度。(作者個人的寬度上限是 120 個字元)
  • 一個原始檔是個階層結構,而非大綱結構。在這個階層結構下的各個階層都是一個視野 (scope)。
  • 為了要讓視野的層次結構更顯而易見,我們根據各行在階層結構下的層級,對原始碼的各行進行了縮排。
  • 如果沒有了縮排,程式在視覺上將無法被人類輕易地閱讀。
  • 一個團隊的開發者,應該要認同某一種編排風格,而且所有團隊的成員都該使用這種編排風格。我們維持一致的軟體編排風格。我們並不想讓它看起來是由一群意見不合的個體所寫成的。
  • 記住,一個好的軟體系統,是由一套具良好可讀性的文件所組成。它們需要有一致且順暢的風格,讀者要能確信,他/她在某個原始檔看到的編排所代表的意義,在別的原始檔也是相同的。

參考書籍

Robert C. Martin 著,戴于晉、博碩文化編譯,無瑕的程式碼:敏捷軟體開發技巧守則,新北市:博碩文化,2013。
譯自:Clean Code: a handbook of agile software craftsmanship

###

熱門文章