🎯 什麼情境該想到我

分工:這張是快速決策卡——什麼時候提煉、名字怎麼取、何時收手。完整的機械步驟、Go 裡提煉時 defer/recover/內聯預算的陷阱、gopls 的支援程度 → 提煉函式(型錄頁);反向操作 → 行內函式。

當你有一段程式碼「可以獨立命名、說明它在做什麼」,或想拆長函式、消重複時。最常用的重構手法。

⚙️ 怎麼用

  1. 找出一段內聚的程式碼(常是「這段在算 X」)。
  2. 抽成新函式,用意圖命名(名字說「做什麼」而非「怎麼做」)。
  3. 把用到的區域變數當參數傳入,回傳需要的結果。
  4. 原處改成呼叫新函式,跑測試確認行為不變。

好處:消除重複、讓函式各層抽象一致、用函式名取代解釋性註解。

★ 什麼時候該提煉:四個訊號

訊號例子
你想在一段程式碼上面寫註解說它在做什麼// 計算運費 下面 8 行 → shippingFee(order)
同一段邏輯出現第三次兩次還可以忍,三次就是一個概念,該有名字(見 工具-DRY原則)
一個區塊的抽象層次跟周圍不同業務流程中間突然出現位元運算或字串切割
你看不懂一段程式碼,花了五分鐘終於懂了立刻把你的理解變成函式名,下一個人就不用再花五分鐘

★ 名字取「做什麼」,不取「怎麼做」

怎麼做(✗)做什麼(✓)
loopAndSumPrices(items)subtotal(items)
checkMapAndDB(id)findUser(id)
parseSplitTrim(s)parseTags(s)

判準:實作換掉之後,名字還正確嗎? findUser 不管查快取還是查 DB 都對;checkMapAndDB 一改實作就說謊。

★ 提煉後要檢查的三件事

  1. 參數個數:提煉出來的函式需要 5 個參數 → 切點選錯了,或這些資料其實該是一個 struct(引入參數物件)。
  2. 有沒有改到副作用順序:原本夾在兩個 I/O 中間的程式碼,搬出去後是否還在同一個時間點執行?尤其是 defer——在新函式裡的 defer 會在新函式結束時執行,而不是原函式結束時。
  3. 呼叫處讀起來像不像一句話:if isEligible(user, plan) ✓;if check(u, p, true, nil) ✗。

★ 何時收手(不要提煉)

  • 提煉出來的函式只有一行,而那一行本身已經很清楚(return a + b)。
  • 讀者每次都得跳進去才能理解呼叫處——名字沒有提供新資訊,只是多一次跳轉。
  • 為了提煉,得把 4 個區域變數改成指標參數傳進去修改:這代表這段邏輯和周圍狀態糾纏太深,先處理狀態(拆資料結構)再提煉。

🧪 我實際套用的紀錄

  • 2026-07-14:(待填)

⚠️ 注意

  • 若抽出後參數爆多,代表切點不對或該連資料一起搬(考慮搬移到別的類別)。

🔗 相關工具