🎯 什麼情境該想到我

當你在 Go 裡設計介面,帶著「先宣告 implements、介面要完整」的其他語言直覺的時候。

⚙️ 怎麼用(步驟 / 公式)

1. 隱式滿足:型別不宣告自己實作了什麼

Go 沒有 implements只要方法都有,就滿足了。
多數介面轉換是靜態的、編譯期就檢查——把 *os.File 傳給要 io.Reader 的函式,不滿足就編不過。

2. 介面要小

只有一兩個方法的介面在 Go 裡是常態,不是例外(io.Reader 只有一個方法)。

3. ⭐ 只為實作介面而存在的型別,不要匯出

如果一個型別的存在只是為了實作某個介面,而且不會有介面以外的匯出方法,就沒必要匯出這個型別

這時建構函式應該回傳介面值,而不是實作型別

// crc32.NewIEEE 與 adler32.New 都回傳 hash.Hash32

好處是換演算法只要改建構呼叫,其餘程式碼完全不動。
只匯出介面也讓「這個值除了介面描述的以外沒有別的有趣行為」變得清楚,還省掉在每個實作上重複寫文件。

4. 用空白識別碼做編譯期介面檢查

有些檢查發生在執行期(例如 encoding/json 用型別斷言看你有沒有實作 json.Marshaler)。這很危險——沒實作到的話 encoder 照樣能跑,只是不會用你的自訂邏輯

要在編譯期釘死,用這個宣告:

var _ json.Marshaler = (*RawMessage)(nil)

介面一改,這個套件就編不過,你會馬上知道要更新。

但不要每個型別都寫。 慣例是只在程式碼裡本來就沒有靜態轉換時才用,而那很罕見。

5. 判斷「有沒有實作」而不需要用它時,用空白識別碼

if _, ok := val.(json.Marshaler); ok { /* ... */ }

🧪 我實際套用的紀錄

  • 2026-08-28:(待填)

⚠️ 注意 / 什麼時候不適用

  • 隱式滿足的代價是「沉默地不滿足」。你以為實作了但簽章差一個字,編譯器不會提醒你——第 4 點就是為了這個。
  • 不要為了「將來可能有別的實作」先抽介面。Go 的慣例是在消費端定義介面(用的人說我需要什麼),而不是在實作端預先宣告。
  • 回傳介面不是無條件的好。它只在「這個型別沒有介面以外的有趣行為」時才對;否則回傳具體型別讓使用者能拿到全部能力,更常見的建議是「接受介面、回傳具體型別」。
  • 這篇來源是 2009 年的文件,泛型(1.18)之後有些抽象可以用型別參數更直接地表達Modern Go Guidelines

🔗 相關工具