函式
一個函式應該只做一件事情
如何定義只做一件事情?
這個函式做的事情,在同一層的抽象概念裡,那就是只做一件事情。
也就是 每個函式只有一層抽象概念
倘若一個函式內,有的在高層次、有的在低層次,這種混合層次的抽象概念,令讀者不容易分辨 (分辨是一個概念還是細節)。倘若將細節與本質混在一起,就會有越來越多細節混雜於函式中。
具描述能力的名稱
一個較長但具描述性質的名稱 > 一個較短但難以理解的名稱
用 switch 的時機:
1.只出現一次 2.用來產生多型物件 3.被藏匿在某個繼承關係下 (上述盡量就好,有的時候還是會違反)
要讓switch只做一件事情很困難,畢竟本質上switch總是在做N件事情,但我們無法永遠避開switch,只能盡量把它埋在低層次。
函式的參數
函式的參數數量,最理想是零個,數量盡量越少越好。除非有特殊的理由,不然無論如何都不要超過三個參數。
從測試的角度看,輸入的參數越多,參數可能的組合就越多,負擔就會越大。
旗標(flag)參數 很爛,不要用。盡量避免將布林變數傳給函式。
單一參數的常見形式
使用單一參數通常有兩個常見的理由:
1.詢問關於這個參數的問題
2.對這個參數進行某種轉換後回傳。
另一個不普遍,但是很有用的單一參數形式 事件
這種形式中,會有一個輸入參數,但沒有任何輸出型參數。整個程式刻意將函式呼叫看做一個事件,並利用參數修改系統狀態。
兩個參數的函式
倘若兩個參數是有序元件組成的單一值,EX: new point(1,2); (直角坐標上本質就是需要兩個參數)
倘若是非有序的兩個參數,可以考慮將順序放進名稱。(可以節省讀者必須跳進function查看順序,所導致的思慮中斷)
需要查看這件事情,應該盡量避免。
三個參數的函式
參數的順序性、看到函式時的停頓、對於參數的忽略等等所導致的問題,都讓人更加難以理解。當要建立三個參數的函式時,請審慎思考。
物件型態的參數
函式參數太多時,可以嘗試將相關的函數包成類別,利用物件去傳遞。
不要覺得這看起來像是在作弊,當他們是某個概念裡的相似部分,這個概念應該獲得一個屬於它們的名稱。
參數串列
public String format(String format, Object... args)
雖然這個函式可以丟很多參數進去,但本質上還是兩個參數,那就OK。倘若本質上超過三個,那就是個錯誤了。
要無副作用
函式保證只做一件事情,卻暗地裡偷偷做其他事情,這就是副作用。
有時會導致奇怪的時空耦合(temporal coupling)和順序相依性問題。
所以務必讓函式只做一件事情,或是在函式名稱讓人知道。
指令和查詢的分離
函式應該是能做某些事情,或是回答某些問題,但不應該同時發生。
如果想要兩件事情都做,應該要拆成兩個函式。
使用例外處理取代回傳錯誤碼
因為回傳錯誤碼,有點違反指令和查詢分離原則,也會導致更深層的巢狀結構。因為當你回傳錯誤碼時,就是要求呼叫者馬上處理。
而使用 例外處理 取代 錯誤碼,就可以將錯誤處理的程式碼從正常的路徑中抽離出來,達到簡化程式碼的效果。
因為錯誤處理就是一件事情,在一個函式只處理一件事的原則下,一個處理錯誤的函式,不應該再做其他的事情。也就是說這個函式應該是 try 開頭,在catch/finally之後就不應該再有其他程式碼。
不要重複自己
如何達到上述的原則?
寫軟體就像寫作,先有想法,再有粗糙的草稿,有了第一份能跑的內容,開始精簡、修改、雕琢,當無法再精簡時,就代表完成了。
Uncle Bob 表示他開始寫函式,一開始總是又複雜又長,有很多縮排&巢狀迴圈,有很長的參數串列,有著隨意的命名,也有重複的程式碼。 (我還以為只有我廢廢的才這樣,高人都是直接下筆如有神助,想好直接寫出神code)
但他也有一整套Unit test,測試範圍涵蓋每一行粗糙的程式碼。
將函式分開、重新命名、減少重複、縮短方法並重新安排順序,甚至會打散類別但保持著單元測試能夠通過。
當函式都符合本書的準則時,就完成了這個函式。
他並不認為有任何人可以辦得到一開始就寫出神Code。
總結
函式簡短、有良好的命名、漂亮的結構。
描述系統的故事,各函式整潔的結合在一起。
Last updated