🎯 什麼情境該想到我
當你「函式的名字沒把意圖說清楚,或參數清單不理想」的時候——修改函式的名字或參數,讓這個介面更容易被理解與使用。改名是這其中最常用、也最有價值的動作。
⚙️ 怎麼用(步驟 / 公式)
意圖:改動函式的名字或參數,讓函式介面更好、更清楚地表達用途。
動作要點(小步安全進行):
- 簡單做法:改動不大、呼叫點不多時,直接改宣告、更新所有呼叫點、測試即可。
- 遷移式做法(migration,適合難一次改完或對外公開的 API):
- 若要調整本體,先把本體整理到能直接搬移的狀態。
- 用提煉函式 Extract Function把函式本體抽成一個具備「新宣告」的新函式。
- 讓舊函式改為呼叫新函式(形成過渡期,兩者並存)。
- 逐一把呼叫端改成呼叫新函式,每步測試。
- 全部遷移完後,用行內函式 Inline Function除掉舊函式,或直接刪除。
- 命名技巧:先想「假如已經寫好、理想的名字會是什麼」,再回頭改實作。
★ 兩種節奏:大步 vs 遷移式
| 大步(simple) | ★ 遷移式(migration) | |
|---|---|---|
| 做法 | 直接改宣告,讓編譯器帶你去每個呼叫點 | 新增新簽章 → 舊的改成轉發 → 逐步遷移 → 刪掉舊的 |
| 適用 | 同一個 module 內、呼叫點你全都改得到 | ★ 匯出的 API、別的 repo 在用、呼叫點上百處 |
| Go 的優勢 | 編譯器是最好的 checklist——改完 go build ./... 列出所有呼叫點 |
遷移式在 Go 的標準寫法:
// Deprecated: 改用 NewClientWithConfig。此函式會在 v2 移除。
func NewClient(addr string) *Client { // ★ 舊簽章保留,只轉發
return NewClientWithConfig(Config{Addr: addr})
}
func NewClientWithConfig(cfg Config) *Client { /* 真正的實作 */ }★
// Deprecated:開頭是 godoc 認得的慣例(pkg.go.dev會把它標成刪除線,
staticcheck 的SA1019會對呼叫端報警)。格式要精確:Deprecated:要在一個註解區塊的開頭自成一段。
★ Go 的關鍵差異:「加一個參數」通常不該真的加參數
// ❌ 每次需求變動就加一個參數 —— 呼叫端全部要改,而且 f(a, true, false, nil) 沒人看得懂
func Serve(addr string, tls bool, timeout time.Duration, logger *slog.Logger) error
// ✅ 選項用 Functional Options(Go 生態的主流)
func Serve(addr string, opts ...Option) error
Serve(":8080", WithTLS(cert), WithTimeout(5*time.Second))
// ✅ 或 params struct(欄位多、且大多會用到時)
func Serve(cfg ServeConfig) error判準見 工具-Go函式參數太多怎麼辦。兩者都讓「加參數」不再是破壞性變更。
唯一的例外——這個參數要加在最前面:
func Fetch(ctx context.Context, url string) ([]byte, error) // ★ ctx 永遠是第一個,名字永遠叫 ctx補 context.Context 是實務上最常見的「改變函式宣告」,而它沒有 options 可以繞。
工具支援(2026-09-25 查證)
- 改名:gopls 的
Rename(VS Code F2)被官方列為最嚴謹的操作之一,會檢查型別安全。 - ★ 改簽章:在
func這個 token 上執行 Rename,gopls 會讓你直接編輯整個簽章並更新所有呼叫點。 refactor.rewrite.removeUnusedParam、moveParamLeft/moveParamRight—— 刪除未使用參數、調整參數順序。- 改套件名:在 import 路徑的套件名上 Rename,會連檔案一起搬;跨子套件要開
renameMovesSubpackages。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
對外公開、無法一次掌握所有呼叫端的 API,用遷移式做法逐步過渡,別硬改。
-
別為了改名而破壞既有語意;名字要反映真正的意圖。
-
★ 改名和改參數不要一起做。分兩次 commit:先改名(純機械、工具可保證),再動參數(要思考)。一起做,review 的人分不出哪個是哪個。
-
★ 匯出 API 用大步改 = 破壞下游。Go module 的相容性承諾是「同一 major 版本內不破壞」——要破壞就得升
/v2路徑。用遷移式。 -
參數改順序最危險:
Copy(dst, src)改成Copy(src, dst),型別相同 → 編譯完全通過,語義整個反了。這種改動要嘛改名(強迫呼叫端面對),要嘛不要做。 -
實作介面的方法不能單獨改宣告——會同時破壞介面與所有其他實作。先改介面,再讓編譯器列出所有實作。
-
反射/
encoding/jsontag/RPC 註冊表不受編譯器保護。靠字串找函式或欄位的地方,改名後是執行期才爆。改前先grep -rn那個字串。
🔗 相關工具
- 引入參數物件 Introduce Parameter Object(把一組常一起出現的參數整併,是改宣告的常見動機)
- 封裝變數 Encapsulate Variable(資料的存取介面版對應手法)
- 重構
- 工具-Go函式參數太多怎麼辦 —— Functional Options 與 params struct 的判準
- 引入參數物件 —— 一組總是一起出現的參數
- 移除旗標參數 ——
bool參數的專門對治 - 神秘命名 —— 觸發改名的壞味道
- 工具-Go命名慣例 —— 新名字該長什麼樣