Clean functions and classes

在讀 Clean Code 的時候,我覺得最讓我印象深刻的就屬 Functions 了。究竟要怎麼樣把 Function 寫得乾淨?我之前一直沒有仔細思考過,可能知道要單一職責、命名清晰,但要把 Function 寫的「夠小」,我還真的沒這麼想過。

小,還要更小

把 Function 寫小有一些明顯的好處,就有點像是文章中的句號、換行般,閱讀時斷點前的資訊會被暫存在大腦中,一個段落結束後喘口氣,稍微消化一下讀過內容,再往下一段去。

也就是說,如果一個 Function 長度有幾十行,甚至上百行,閱讀的壓力就會增加。那麼,怎麼樣的 Function 才稱「小」呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function isEmpty(value) {
if (value == null) {
return true;
}
if (
isArrayLike(value) &&
(Array.isArray(value) ||
typeof value === "string" ||
typeof value.splice === "function" ||
isBuffer(value) ||
isTypedArray(value) ||
isArguments(value))
) {
return !value.length;
}
const tag = getTag(value);
if (tag == "[object Map]" || tag == "[object Set]") {
return !value.size;
}
if (isPrototype(value)) {
return !Object.keys(value).length;
}
for (const key in value) {
if (hasOwnProperty.call(value, key)) {
return false;
}
}
return true;
}

這樣的 Function 夠簡潔嗎?以上節錄 lodash 這個 library 的 isEmpty.js

總共 27 行(原檔案 20 出頭,這裡是 format 過後的檔案),雖然不算少,但也不算太長了吧?但以作者的標準來看,這樣的 size 其實很大!如果我們稍微改寫一下呢,把這個 Function 拆開來組裝。

1
2
3
4
5
6
7
function isEmpty(value) {
...
if (isArrayLike(value) && isTypeHasLengthProp(value)) {
return !value.length;
}
...
}

我們將 Array.isArray(value) || typeof value === 'string' 等等的一連串判斷式簡化成一個 Function(這樣命名不一定精確,但概念是簡化多個表達式成一個 Function),如此一來,在可讀性上就會好上一些。

更簡化一些,可以將 if, for, while 等結構直接轉換成 Function。如果能將程式碼縮減到個位數的行數,會大大的降低看一次段落的壓力,需要細節的時候,讀者再往更深入的 Function 去看就好。

(未完待續)

參考資料

  1. GitHub loadash - isEmpty.js