# 函式

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

如何定義只做一件事情?

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

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

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

## 具描述能力的名稱

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

## 用 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。

## 總結

函式簡短、有良好的命名、漂亮的結構。

描述系統的故事，各函式整潔的結合在一起。
