🎯 什麼情境該想到我

當你在猶豫「這個參數該收具體型別還是介面」,或「測試時怎麼把資料庫/HTTP 換掉」的時候。
★ 但在 Go 裡,這條原則有一個和 Java 相反的結論——那是本卡的重點。

⚙️ 怎麼用

原則本身

依賴抽象,不依賴具體實作。 呼叫端只認介面,不認是誰實作的。

三個實質好處:

  1. 可替換(換套件、換演算法、換供應商)
  2. ★ 可測試(換成測試替身,不碰網路與資料庫)
  3. 編譯期解耦(改實作不用重編依賴它的套件)

★ 介面該定義在哪一邊(最重要的一個判斷)

❌ Java 式:實作方定義介面,並「宣告我實作它」
package storage
type Storage interface { Get(...); Put(...); List(...); Delete(...); Stat(...) }
type S3 struct{}      // implements Storage

package app
func Process(s storage.Storage)     // ★ 被迫依賴一個大介面,只用得到 Get

✅ Go 式:★ 消費方定義介面,只放它真正用到的方法
package app
type Getter interface { Get(ctx context.Context, k string) ([]byte, error) }   // ★ 只要一個方法
func Process(g Getter) { ... }

package storage
type S3 struct{}                    // ★ 完全不知道 app.Getter 存在,但天然滿足它
func (S3) Get(...) ([]byte, error)

★ Go 的隱式介面滿足讓「消費方定義介面」成為可能,而這帶來三件事:

  1. 介面自然會很小(你只寫你用得到的方法)
  2. 實作方不需要 import 消費方(依賴方向乾淨)
  3. 別人的型別(第三方套件)也能滿足你的介面,不用改對方程式碼

這就是 Go 諺語 “accept interfaces, return structs” 的由來。
Java 的「介面放在實作旁邊」在 Go 裡是反模式。

★ 「只有一個實作」要不要抽介面

情況抽不抽
只有一個實作,且測試不需要替換它(純計算)❌ 不抽。直接用具體型別
只有一個實作,但測試需要替換(會打網路/資料庫/檔案/時間)✅ 抽 ——★ 測試需求本身就是正當理由
已經有兩個以上實作✅ 抽
「以後可能會有第二個」❌ 不抽。YAGNI ——真的出現時再抽,那時才知道正確的邊界

★ 最常見的過度設計:為每個 struct 配一個同名介面(UserService + IUserService)。
這在 Go 裡沒有任何好處——它不會讓程式更解耦,只是多一層間接與一個要同步維護的檔案。

實務:Go 的三個標準介面

io.Reader   // 一個方法,全世界都能滿足
io.Writer
error       // ★ 只有 Error() string

★ Go 標準庫最成功的抽象都只有一到兩個方法。 這不是巧合——
介面越小,能滿足它的型別越多,組合的可能性越大。(見 工具-Go介面小而隱式)

TypeScript 的對應

// TS 也是結構型別,同樣「消費方定義」最好
type Getter = { get(key: string): Promise<Uint8Array | null> };
 
export async function process(g: Getter) { ... }
// ★ 任何有 get 方法的物件都能傳進來,連 class 都不用

🧪 我實際套用的紀錄

  • 2026-07-14:(待填)

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

  • ★ 介面太大 = 沒有介面。一個十個方法的介面,只有原本那一個型別滿足得了——
    它沒有提供任何替換性,只是把具體型別換了個名字。
  • ★ 不要為了「看起來解耦」而抽。判準是**「有沒有第二個實作,或測試需不需要替換」**。
  • 回傳介面通常是錯的(Go):回傳具體型別讓呼叫端拿到完整能力,要不要縮成介面由它決定。
    例外:回傳的型別真的有多種(sql.Driver)、或要隱藏內部型別。
  • 介面會掩蓋效能特性:io.Reader 背後是記憶體還是網路,呼叫端看不出來。
    效能敏感的地方要在文件裡講清楚。
  • 動態分派有成本(無法內聯)。極熱的迴圈上,具體型別更快——但先量再說。
  • 不要把介面當文件。介面描述「能做什麼」,不描述「保證什麼」(複雜度、是否阻塞、是否並行安全)——那些要寫在註解裡。

🔗 相關工具