函式

一個函式應該只做一件事情

如何定義只做一件事情?

這個函式做的事情,在同一層的抽象概念裡,那就是只做一件事情。

也就是 每個函式只有一層抽象概念

倘若一個函式內,有的在高層次、有的在低層次,這種混合層次的抽象概念,令讀者不容易分辨 (分辨是一個概念還是細節)。倘若將細節與本質混在一起,就會有越來越多細節混雜於函式中。

具描述能力的名稱

一個較長但具描述性質的名稱 > 一個較短但難以理解的名稱

用 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