2014-04-16 54 views
0

即使使用DI,我們的業務/服務類型也需要在其方法中創建一些傳遞對象。我會說這些傳遞對象始終是值類型(表示純數據)或I/O類型(表示外部狀態)。值類型可以更新,但我們希望在測試中模擬/存根的I/O類型,所以我們不能直接創建它們。如何在運行時使用依賴注入來創建I/O類型(例如文件)

我看到的通用解決方案是給類提供某種IOFactory依賴關係:在生產中,我們爲產生真正I/O對象的類提供了一個工廠;在測試中,我們爲產生假I/O對象的類提供了一個工廠。

我不喜歡這個,不僅需要創建模擬/存根I/O類型,還要爲實際​​類型和替代品創建工廠。這讓人感覺很累,特別是在像JS這樣的動態語言中,我經常可以輕鬆地爲每個測試創建我的模擬/存根(stub)。

發生對我來說是使用注射器有點像一個服務定位器的替代...

var file = injector.inject(File, '/path'); // given type, returns new instance of that type 

......這樣,在生產,噴油器被配置爲提供一個真正的文件,而在測試中,它被配置爲返回一個模擬/存根。我們可以將注入器視爲一種特殊的全局注入,但可以說注入器現在是需要使用它的每種業務類型的依賴關係,因此它應該像任何其他依賴注入一樣。

我認爲贊成這個想法的主要觀點是,在許多情況下噴油器可以減少工廠樣板(以一些額外的工廠配置工作爲代價)。什麼是反對的論據?工廠是否更好,因爲它們是對類所需要的更具體的聲明,從而用作文檔?或者是完全不同的正確解決方案?

回答

1

在使用injector作爲Service Locator

服務定位器有時被描述爲being an anti-pattern,因爲:

  • 使用它錯誤地導致代碼很難維護
  • 這是非常容易錯誤使用

(一些開發商raise objections約稱這是反模式,但仍普遍認爲,它有特定的目的,常常被誤用。)

,你描述的是反模式的一個很好的例子的injector。有了它,你的對象現在會有一個隱藏的所需的依賴 - 一個沒有在其構造函數中聲明的依賴。

如果注入器在對象使用它時未配置,則會發生運行時錯誤。可能有人可能沒有意識到需要這種配置才能使對象正常工作(您現在可以在6個月後使用甚至可能是那個人)。

依賴注入背後的想法是,一個對象是非常明確的,它需要的行爲如預期。這背後的原理與用於接口準則的相同:Easy to use correctly; Hard to use incorrectly

你說得對,有時需要引入工廠才能動態實例化對象,這很麻煩,但是很多這種樣板通常植根於許多面向對象語言的冗長語法;不一定在依賴注入的概念中。

所以,這是確定使用短期方便(就像任何其他的全局變量)的服務定位器的方法 - 只要你意識到這所有它真的提供了這樣的情況。


至於替代方案,不要忘記所需的依賴不一定需要作爲構造函數參數傳遞。

而不是將工廠傳遞給類的構造函數,有時使用Factory Method方法是有意義的。也就是說,強制派生類提供依賴關係,而不是期望它來自對象的創建者。

如果SUT可以使用有意義的默認依賴關係進行初始化(例如,Null Object),則可以考慮在setter方法中注入依賴關係。

在C/C++中,開發人員有時甚至依靠鏈接器來處理依賴注入。在他的書中,Test-Driven Development for Embedded C James Grenning寫道:

爲了打破生產代碼的依賴關係,只考慮協作者的接口。 [...]接口由頭文件表示,實現由源文件表示。 LightScheduler [SUT]在鏈接時綁定到生產代碼實現。

單元測試通過提供替代實現來利用鏈接接縫。 (第120頁)

最後,問一下你希望從依賴注入中獲得什麼。如果收益不超過實施它所涉及的工作,那麼也許沒有必要。但是不要爲了在短期內節省一點時間而放棄它。

+0

謝謝。我想我找到了答案:更好地讓類給出關於它們依賴關係的特定信息。抽象工廠並不完全具體,但它們比噴油器更具體。這可能對於由一個或兩個人編寫的單個程序中的代碼而言並不重要,但將該注入器用作服務定位器(即使只是針對I/O類型)對於該類的任何外部消費者來說都是可怕的。 – Jegschemesch

+0

你說得對,這是一個語言問題:我真正想要的是創建樣板抽象工廠的簡便方法。 – Jegschemesch

相關問題