前言
本文是看完 The Art of Clean Code 這個演講後的心得整理,文章並未涵蓋演講的所有內容,僅就個人有興趣(其實是聽懂的部分)加以整理。
講者的演講綱要
- Introduction (Why)
- Names (The power invested in you)
- Functions (SRP)
- Classes (The OOP Utopia 物件導向鳥托邦)
- Commants (Incompetence)
- Clean Lambdas (Handling a lightsaber)
講者的演講的綱要如上,以下的心得並不一定依照這個順序,而且講者最後一個小節是說明 JAVA 語言的撰寫技巧,就沒有特別詳細聽完。
開發痛點
Clean Code 是解決方案,而對於解決方案我總是想到「Mission: Impossible」有關人造病毒的那集提到的「因為有反派的存在,英雄才會出現,所以有病毒的存在,則解藥才有價值」的一段話(好啦,應該沒有這段對白,但大致是這個意思),所以要先瞭解痛點,或許在你目前的環境根本沒有這樣的情形,則 Clean Code 是什麼就完全不重要。
有價值的特性才算是優點
「瞭解標的程式的意圖」是維護既有系統的必要過程,不管她是別人撰寫的或是過去的自己撰寫的,在開始維護或除錯之前總是先得清楚程式怎麼會長成這樣,才好決定是否下手及如何下手。
所以一段程式被撰寫可能只花一次的時間,但可能花上十次,甚至百次的時間被閱讀,但這個閱讀時間的投入並無法創造最終使用者的價值,所以可以說是一種浪費,因此 Clean Code 試圖解決的痛點之一就是。
「降低浪費在理解程式的資源」
易於閱讀
Martin Fowler 在「重構」一書中說到「任何人都能寫出電腦能看得懂的程式,但少數程序員知道如何寫出人看得懂的程式」這段話,可以說,電腦能夠看懂的程式就像文言文體,需要一定的門檻及時間人才能讀懂,而能將程式寫得像白話文體,普羅大眾都能快速理解,且多數人看過後沒有太多的理解落差的工程師則是稀少的,而且很容易被誤以為是剛出新手村,而這又是另一層次的故事。
如何著手
講者從以下幾個地方說明,如何讓程式更容易被理解。
命名
你如何理解一個類別、函式或變數的功能?是透過歷史久遠的註解、被誰呼叫使用、自行考古鑽研或是以上皆是?為了團隊的伙伴更為了未來的自己,何不透過最簡單且直接的方法「名稱」來讓各個物件向你自我介紹。
-
表達動態與靜態
在物件導向開發方法論之中,類別被視為一個「靜態」的物件,而函式則為其「動態」的行為,所以命名時,應將類別以「名詞」命名,函式以「動詞」開頭的動作表達。-
類別命令時可以採用「擬人」或「擬物」方式,代入特定職務或物件概念,例如以「IncomeStatement」表現會計領域的「綜合損益表」或是將系統整體視為一個專案團隊,不同類別是其中的不同成員,而各個介面則是不同職務角色。
-
函式命名時採用動詞,但也不要只有動詞,加入動作相關的名詞例如:searchProduct() 就比 search() 來得容易理解函式的任務為何。
-
-
避免無意義的名稱尾綴
例如OrderInfo 及 OrderData 的尾綴「Info」及「Data」若不具備特殊意義,僅在表達其為「資料」則採用 Order 命名即可。 -
符合口語慣例
例如 Yes/No 問句的 IsGoldClient() 函式,看起來像是確認客戶端是否為黃金客戶,則回傳值就應該是布林值,而不該回傳客戶的特性「Green」字串,這樣就像口語慣例不符。 -
要能發音
如果設定了難以被發音或無法發音的名稱,在口語溝通時會有困難,容易浪費時間解釋,甚或為了避免發音而避免提到。 -
採用關係人的用語
要寫出易懂的程式,除了撰寫的技巧之外,對於「命名」這件看似不起眼的小事,其實影響深遠,若是能夠對於系統的應用領域有一定理解(職務分工上或許是由需求分析人員負責),在命名時採用關係人的用語,例如最終使用者來說,符合其商業流程的用語,或是該專案領域的專有名詞,例如會計中的借貸、分錄或過帳等用語,如果程式中的類別名稱或函式名稱採用對應的用語,在之後維護與溝通上必然較為直覺。 -
避免縮寫
目前各項開發工具對於長名稱的支援功能都很完備,不太需要因為考慮輸入錯誤或減少輸入時而特別縮短名稱,因此,除非該縮寫已是商業領域的共識,沒用縮寫反而令人混淆(例如某些縮寫的全名反而不是那麼經常被提起,比如 FBI, CIA )。 -
維持一致
對於相同概念採用相同文字,例如:find, fetch, get 都能夠表達「取得某項物件」概念的動詞,就該在具備相同概念的名稱上採用相同單字。
另外,對於商業流程中的同一個事物,在處理時也保持相同名稱,例如對於「客戶」這個商業流程概念,若採用 Customer 描述,就應避免在資料表或系統的不同段落使用 Client 或 Buyer 表達,除非在商業流程中有其特殊性,而不該以其在系統中具備的特殊性套用不同名詞。 -
重新命名
此外對於目前已經存在的名稱,應該隨時反思是否仍能適切的表達其任務,不該拘泥於原始名稱而不去異動,而使得名稱與其實際內容不符,目前的各種 IDE 工具對於更名的相關支援其實水都能夠做到相當完備,大概僅有極少如 XML 設定或是透過反射技巧的部分可能出現問題。
更多命名的建議可以參考這裡對 Clean Code 這本書在命名的心得,另外也可以參考這本我的程式碼會說話。
註解
註解也是無法提供使用者價值的產出,所以也是應該降低投注其上的資源,註解最容易犯錯也最重要的一個原則就是:
註解不是在描述「做什麼」而是應該說明「為什麼」這麼做
理想狀態下,應該透過程式碼本身傳達及溝通,因為程式碼才是最即時的文件及具價值的產出,因此透過註解來表達程式意圖,永遠都該是難以透過程式自身表達時,最後才考慮的溝通手段。
寫註解是因為沒有辦法透過程式表達意圖時最後手段
在以下幾種就是在需要時難以透過程式碼來傳達的資訊
- 規格(連結)/除錯說明
透過註解來串通程式與規格文件或錯誤回報的原始資料,提供「為什麼這麼寫」的直接因素,提供維護人員從樹到林的資訊連結,這樣的資訊目前在版控系統中也有提供建立關連,但是直接透過註解來取得,還是較透過版控系統查詢直接,除非規格文件或錯誤回報是直接整合版控系統,且每次的簽入都能做到單一目的(特定工單)。 - 澄清資訊
程式中某些看冗餘或突兀的呼叫,例如說明調用特定 API 的原因,提供避免後續維護時誤刪必要呼叫,同時提供後續維護評估是否仍需保留的資訊。 - 必要提醒
說明程式撰寫方法可能隱含的前題或方法的優缺,例如提醒該用法語法較簡約但可能有執行緒安全的情形需要注意。 - 待辦事項
程式重構過程中,尚未能到位,或尚未需要進行的異動,透過待辦事項與接手的維護人員溝通,提供建議及想法,例如此次尚未處理,但可讓程式更具彈性或易讀的修改建議。 - 公開文件
例如自行開發的函式庫或框架時,所撰寫的 API 說明文件,透過類別串連文件,提供正確的使用及擴充方法。
尺寸大小
對於可處理的資訊量當然是由人而異,但只要是透過團隊開發,就不該以自己當下能處理的資訊量來決定函式的大小,而是提供一個普遍都能處理的尺度。
這裡提出函式的理想大小是寬不大於120的單字,高在10行以內,至少應該在不捲動的情形下可以完整的看到整個函式,而單一個類別應該少於三百行程式碼。這樣絕對的數字,個人主觀的認為是參考用即可,主要在於提醒不要養出巨型函式及類別。
單一職責
-
避免在程式中間跳離
尤其在數千行的單一函式中,若隨時都可能跳離函式,那對於除錯就是惡夢,不但要逐一設定中斷點,對於迴圈及判斷的跳離點更要逐一注意,大部分除錯的時間可能都是在無意義的下一步中消耗,因此應統一固定在程式的最後才跳離函式。 -
傳入物件是否需要檢查其為空物件
僅在邊界(例如類別的公開方法)進行檢查(內部函式皆不檢查,因為公開方法會保證傳入物件的狀態)模式,至少可以將檢查這個行為的干擾限縮在少數幾個入口並取得與檢查的之間的平衡。 -
迴圈及判斷的巢狀數應三層以內
多層次的巢狀迴圈基本就容易效率瓶頸不說,在除錯時要時刻注意所在的位置,內外層迴圈的參數交互作用都容易形成理解的混淆。
結語
講演中還有一些其他的內容,大家可以直接去看看,本文是以個人主觀就「容易閱讀」為核心加以整理,跟大家分享也提醒自己,也推薦看看 Clean Code 這本書本人。