「大哉問」- 什麼時候該抽 interface
今天與同事在群組聊一個「很短卻很大」的問題:「什麼時候該抽 interface」。
我說過,這種短短的問題很可怕,因為 Context 很少,所以其實很難回答。
為什麼會有這個討論,起因是同事為了一些理由,抽了一個 Interface,並由一個 Mediator 來在 Runtime 時決定實作要選哪一個。這是一個不錯的設計,也符合 DI 原則,但我看完後建議他們不要這麼做,因為他們要解決的問題有更輕量的做法。
結束後,同事就問了我「什麼樣的狀況下適合抽 Interface」這個超級大的問題。
維基 vs 大師
要回答問題前,我們先來查查維基百科,看 DI 說了什麼:
看起來就是叫我們不要直接依賴實作(Concrete Implementations),要多多依賴介面(Interfaces)囉?
可是,當你乖乖地把所有依賴都加一個介面時,或是再乖一點,有 Class 就加 Interface 時,大師又告訴你不要這麼做,因為這是一個壞味道:Header Interfaces。
嘖!
現在是怎樣?到底是要抽還是不要抽啊?
Problem 與 Solution
有設計,肯定是有它要解決的問題。說到底,DI 到底解決了什麼問題?我認為它在不同場景解決了不同問題,但歸納起來不出兩種:
- 多實作的需求
- 偶然的複雜度
首先,你系統中有沒有一些事情,是依照當下情況不同,而有不同做法,但流程是共用的?肯定是有,對吧?
例如,當用戶存入一筆錢進戶頭,你要發推播給用戶手機、要為用戶更新等級、要通知信貸推廣部加入用戶資料(他有錢還了,趕快問他要不要借錢)… 你有很多 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
- https://martinfowler.com/bliki/HeaderInterface.html
- https://martinfowler.com/bliki/RoleInterface.html
- https://blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions/
- https://www.amazon.com/-/zh_TW/David-Farley/dp/0137314914
- https://www.tenlong.com.tw/products/9786263332645
More about Me
- 更多軟體工程相關討論:https://www.facebook.com/kukumamaya
- 我的YouTube 頻道:https://www.youtube.com/@yu-songsyu6598
- 沒時間寫測試?沒時間重構?你就是不寫測試才會沒時間: https://www.tenlong.com.tw/products/9786263332645?list_name=lv