「大哉問」- 什麼時候該抽 interface

Yu-Song Syu
Kuma老師的軟體工程教室
6 min readJan 26, 2024

--

今天與同事在群組聊一個「很短卻很大」的問題:「什麼時候該抽 interface」。

我說過,這種短短的問題很可怕,因為 Context 很少,所以其實很難回答。

為什麼會有這個討論,起因是同事為了一些理由,抽了一個 Interface,並由一個 Mediator 來在 Runtime 時決定實作要選哪一個。這是一個不錯的設計,也符合 DI 原則,但我看完後建議他們不要這麼做,因為他們要解決的問題有更輕量的做法。

結束後,同事就問了我「什麼樣的狀況下適合抽 Interface」這個超級大的問題。

維基 vs 大師

要回答問題前,我們先來查查維基百科,看 DI 說了什麼:

看起來就是叫我們不要直接依賴實作(Concrete Implementations),要多多依賴介面(Interfaces)囉?

可是,當你乖乖地把所有依賴都加一個介面時,或是再乖一點,有 Class 就加 Interface 時,大師又告訴你不要這麼做,因為這是一個壞味道:Header Interfaces。

嘖!

現在是怎樣?到底是要抽還是不要抽啊?

Problem 與 Solution

有設計,肯定是有它要解決的問題。說到底,DI 到底解決了什麼問題?我認為它在不同場景解決了不同問題,但歸納起來不出兩種:

  1. 多實作的需求
  2. 偶然的複雜度

首先,你系統中有沒有一些事情,是依照當下情況不同,而有不同做法,但流程是共用的?肯定是有,對吧?

例如,當用戶存入一筆錢進戶頭,你要發推播給用戶手機、要為用戶更新等級、要通知信貸推廣部加入用戶資料(他有錢還了,趕快問他要不要借錢)… 你有很多 Follow Up 要執行。這些 Follow Up 視情況會新增或減少,而且不歸你這部門管,你的工作就是「一個一個通知到位」。這時,把這些 Follow Up 做成 Interface: AddDepositFollowUp 的實作,你就可以用一個 For 迴圈通知到底了。

再例如,當用戶想存入一筆錢進戶頭,你要先檢查此戶頭是否被凍結,也要先看有沒有超過單日跨行存款上限,還要看用戶有沒有為這個戶頭設下風控規則(外幣戶頭匯率是否超出特定範圍)…等。有很多事先檢查得做,但不同情況下檢查項目不同之外,這些還是不歸你這部門管。這時,把這些 Pre-check 做成 Interface: AddDepositPreCheck 的實作,你就可以用一個 For 迴圈檢查到底了。

因此,有共用流程的需求,加上會長大的實作數,這時就很適合抽 Interface。

其次,你的系統中更常見的,應該是「從 DB拿一個 Entity 出來,叫它做些事,再存回 DB 中」,對吧?

在 David Farley 的 Modern Software Engineering 一書中,作者把「讀取、寫入」這種對 I/O 的依賴,稱作是「偶然的複雜度」。它會增加複雜度,但跟你的主要業務無關。例如剛剛的存錢例子,你把資料從 MySQL 搬到 PostgreSQL,對用戶來說應該是完全無感的 — 那不是你服務的功能。因此,讓主流程對管理 MySQL 連線的 Client 直接依賴並不合適。即便它只有一個實作,抽 Interface 還是蠻適合的。

更何況,你以為儲存空間不會換?其實會唷!在拙作:你就是不寫測試才會沒時間:Kuma 的單元測試實戰 — Java篇 中,對 I/O 的抽換場景有專門的段落講解,這裡就不贅述了。

考慮抽換

Interface + DI 有個很大的好處,就是「方便抽換」。

如果你是 Clean Architecture 或六角架構,你一定會遇到「由內向外」與「由外向內」兩種方向的依賴,以 Clean Architecture 為例,你有沒有想過為什麼 Uncle Bob 不希望你在 Use Case 層依賴從事 I/O 的 Adapter 層,而要你「墊」一層 Interface?原因就是 I/O 是運行細節,並不是業務邏輯,不要被細節綁架了。

那外向內呢?例如,AddDepositController 對 AddDepositUseCase 有沒有必要多墊一層 IAddDepositUseCase 的 Interface?我認為是「可以但沒必要」。首先 Use Case 也不管 I/O。其次,存錢的邏輯是非常核心且抽象的,它幾乎不會遇到有多實作的機會,也就是你不用抽換它。因此,為 AddDepositUseCase 多墊一層 Interface,我認為是「可以但沒必要」的。

總結

考慮兩件事:1) 多實作的需求,2) 偶然的複雜度,除此之外你不太會需要抽 Interface。

Reference

  1. https://martinfowler.com/bliki/HeaderInterface.html
  2. https://martinfowler.com/bliki/RoleInterface.html
  3. https://blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions/
  4. https://www.amazon.com/-/zh_TW/David-Farley/dp/0137314914
  5. https://www.tenlong.com.tw/products/9786263332645

More about Me

--

--