🎯 什麼情境該想到我
當你「一個函式的本體跟它的名字一樣直白、沒帶來額外價值」,或是一群函式被切得太碎、間接層多到反而難懂的時候——把呼叫處直接換成函式本體,讓邏輯回到一起再重新整理。
⚙️ 怎麼用(步驟 / 公式)
意圖:當函式本體和名字一樣清楚時,去掉這層間接,把呼叫處換成本體。
動作要點(小步安全進行):
- 確認這不是多型方法(子類別有覆寫的就不能行內)。
- 找出所有呼叫這個函式的地方。
- 把每個呼叫逐一換成函式本體,每換一處就編譯、測試。
- 全部替換完成後,刪除原函式定義。
- 若替換過程變複雜(例如本體用到呼叫端沒有的東西),一次處理一個呼叫點,別急。
★ before / after(Go)
before——函式名沒比本體更清楚,只是多一層:
func rating(d *Driver) int {
if moreThanFiveLateDeliveries(d) { return 2 }
return 1
}
func moreThanFiveLateDeliveries(d *Driver) bool { return d.LateDeliveries > 5 }after——本體本身就是最清楚的說法:
func rating(d *Driver) int {
if d.LateDeliveries > 5 { return 2 }
return 1
}什麼時候該 inline(三個真實訊號)
| 訊號 | 說明 |
|---|---|
| 名字沒比本體清楚 | isEmpty(s) 的本體是 len(s) == 0 —— 名字沒帶來理解價值 |
| ★ 一群組織不良的小函式 | 先全部 inline 回一大坨,再重新提煉出對的切點。這是 Fowler 明說的用法 |
| 中間人:只轉發不做事 | func (s *S) Name() string { return s.inner.Name() } 一路轉發 5 層 |
★ Go 的陷阱:具名回傳值 + defer
func load(p string) (b []byte, err error) {
defer func() {
if err != nil { err = fmt.Errorf("load %s: %w", p, err) } // ★ defer 改了回傳值
}()
return os.ReadFile(p)
}把這種函式 inline 掉,會靜默丟掉錯誤包裝。 呼叫端的 errors.Is 還會通,
但錯誤訊息裡少了 load <path>: 這段脈絡——是行為改變,編譯器不會抗議。
另外兩個必查項:
- 匯出函式不能隨手 inline —— 別的 module 可能在呼叫。先確認呼叫者範圍(
grep -rn,或 IDE 的 Find References)。 - 介面方法不能 inline —— 實際跑哪一份是執行期決定的。
⚠️ 不要和「編譯器 inline」搞混
這裡的 inline 是原始碼層的重構;Go 編譯器另外有自己的內聯優化,兩者無關。
實測 Go 1.26.4(2026-09-25):
./main.go:5:6: can inline small with cost 4
./main.go:7:6: can inline big with cost 73 ★ 含 for 迴圈的函式現在也能被內聯
./main.go:24:6: cannot inline main: function too complex: cost 163 exceeds budget 80
→ 你手動 inline 一個函式,不會讓程式變快;編譯器早就決定好了。
工具支援
gopls 有 refactor.inline.call,而且官方把 Inline 和 Rename 並列為「嚴謹」的操作(對比 Extract 被明說不嚴謹)。
但同樣有掉註解的問題(go.dev/issue/20744)。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
若函式名字確實解釋了意圖、或會被多型覆寫、或呼叫點很多且分散,就不要行內。
-
常見用途是先把一堆爛的小函式行內回一大段,再重新用提煉函式 Extract Function切出更合理的邊界。
-
反向手法:提煉函式 Extract Function。
-
★ 具名回傳值 +
defer改回傳值的函式,inline 掉會靜默改變行為(見上)。 -
遞迴函式、被多處呼叫的函式、匯出的 API —— 不要 inline。
-
inline 會讓呼叫端變長。目的是「換掉一個壞的切點」,不是「減少函式數量」。若 inline 完沒有接著重新提煉,通常是白做。
-
一次只 inline 一處,跑一次測試。批次 inline 十處然後測試紅了,你不知道是哪一處。
🔗 相關工具
- 提煉函式 Extract Function(本手法的反向操作)
- 行內變數 Inline Variable(同樣是去掉不必要的間接,只是對象是變數)
- 重構
- 中間人 —— 「只轉發不做事」這個壞味道的對治就是本手法
- 內聯類別 —— 類別層級的同一件事
- 行內變數 —— 變數層級的同一件事
- 工具-小步重構 —— inline 也要一步一測