跳至主要内容

[note] Clean Code

Naming

好的命名能夠讓開發者不用去看內部的實作,就能理解它的意思。

  • getUser() 可能太過籠統,視情況需要的話可以使用 getUserByEmail()

Functions

抽成 function?

可以思考下面這兩種寫法:

1

在一般的情況或還無法確定未來的情境會不會變的更複雜前,會傾向使用左邊的寫法,邏輯一目了然;相較之下,右邊的情境會讓開發者需要跳到另一個函式中才能確認該判斷式實作的內容,增加了開發者的認知負擔。

但右邊的寫法並非沒有任何好處,當這個判斷邏輯:

  1. 會在多個地方被使用時,抽成 function 可以避免開發者拼錯字。雖然可以透過將該單字抽成 CONSTANT 來避免拼錯字的問題,但開發者還是會需要在使用的地方 import 這個 constant,多一行 import 也有點煩,所以抽成 function 可以避免這個問題(如果有使用 TypeScript 也可以直接避免拼錯字的問題)。
  2. 當這個判斷的邏輯變得複雜時。如果判斷邏輯不像是 status === 'fulfill' 這麼單純的話,抽成 function 會是合理且必要的選擇
  3. 可以針對這個邏輯進行 unit test

Control Structures & Errors

Avoid Deep Nesting: Use Guards & Fail Fast

原本的寫法:

function withoutGuard() {
if (isValid) {
// do a looooot of stuff
}
}

改成 early return:

function withGuard() {
if (!isValid) {
return;
}
// do the same stuff here
}

另外,使用 Strategic Pattern、或把 function 保存在物件(Map)中,也是很好用來處理 nested if 的情況:

const orderStatusProcessor = {
success: () => {/*... */},
failed: () => {/*... */},
pending: () => {/*... */},
};

function processOrder(order) {
// 執行 orderStatusProcessor
orderStatusProcessor[order.status]();
}

Error Handling

如果它是個錯誤,就把它以錯誤拋出,而不是直接 return 它。

沒有使用 error:

if (!isValid) {
return { code: 111, message: 'it is invalid' };
}

使用 error 處理:

if (!isValid) {
const error = new Error('it is invalid');
error.code = 111;
throw error;
}

如果所有 error 的格式都是一樣的,則可以在某一個地方統一使用 try...catch 來處理這些錯誤。

Using Factory Functions & Polymorphism

Factory Function 的概念比較像是會回傳一個「東西」的函式,有點類似 class。

Reference