設計原則:小議 SPI 和 API


背景

第一次聽說 SPI 是閱讀《軟件框架設計的藝術》,以后陸續在 Log4Net 和 Quartz.Net中發現了以這種形式組織代碼的方式,本位給出為什么要區分 SPI 和 API 的一個思考過程。

從面向接口編程說起

我們在“調用方”和“實現方”之間引入了“接口”,上圖沒有給出“接口”應該位於哪個“包”中,從純粹的可能性上考慮,我們有三種選擇:

  1. “接口”位於“調用方”所在的“包”中。
  2. “接口”位於“實現方”所在的“包”中。
  3. “接口”位於獨立的“包”中。

下面讓我們依次分析這三種可能性,如果現實中確實有這種可能性,不如我們就為其起個名字以方便交流。

“接口”位於“調用方”所在的“包”中

我們先想象一個場景,以倉儲的接口為例:

我們將“倉儲接口”放置於“領域層”這個“包”中,實現放在一個獨立的“包”中,我們看DDD大師的實現都是這樣子,現在來思考一下為什么這么做。

“領域層”的“領域服務”會依賴“倉儲接口”,“倉儲接口”也會依賴“聚合根”,這兩者都是除了“實現依賴”之外的依賴關系,如果將“接口”放到“倉儲實現”中就喪失了面向接口編程的意義(編譯也不會通過),如果放到“獨立層”中呢?會編譯不通過,出現雙向依賴了。

對於類似這種情況下接口,我們將其稱為“SPI”,全程為:service provider interface,“SPI”的規則如下:

  1. 概念上更依賴調用方。
  2. 組織上位於調用方所在的包中。
  3. 實現位於獨立的包中。
  4. 常見的例子是:插件模式的插件。

“接口”位於“實現方”所在的“包”中

我們先想象一個場景,以Unity提供的IUnityContainer接口為例,除了維護這個框架的團隊之外,我們沒有發現誰實現了這個接口,雖然理論上是可以實現這個接口的(如果能實現的話,我們何不自己弄額Ioc容器呢?)。

對於類似這種情況下的接口,我們將其稱作為“API”,“API”的規則如下:

  1. 概念上更接近實現方。
  2. 組織上位於實現方所在的包中。
  3. 實現和接口在一個包中。

“接口”位於獨立的“包”中

這里就不說場景了,如果一個“接口”在一個上下文是“API”,在另一個上下文是“SPI”,那么你就可以這么組織。

需要注意的事項

不管是SPI或API,接口都是可以組織到獨立的“包”中,這么做是否有意義,自己來做出決定了。

SPI和API也不一定是接口,我這里都是指狹義的具體的接口。

另外一張圖

備注

每一次思考都伴隨着收獲,也離不開和朋友們的交流,天更藍了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM