2012-08-17 125 views
4

我一直在閱讀依賴注入,並且我理解如何在XML中指定依賴關係的吸引力,就像在許多框架中所做的那樣。我在一個大型系統上工作,在這個系統中,我們通常會調用工廠來獲取具體對象,並且正在努力理解爲什麼手動注入的依賴項爲什麼in this Wikipedia article被認爲更好。爲什麼依賴注入比使用工廠更好?

在我看來,調用一個工廠是更好,因爲:

  1. 調用代碼不需要知道或關心一個特定的依賴關係存在。
  2. 調用代碼並不需要改變,如果新的依賴被添加到被叫方。
  3. 調用代碼並不需要有專門爲選擇具體的實例來注入任何邏輯。

在我看來,這種依賴注入僅提供好處,當調用代碼決定對依賴具體的類。幾乎就像「這是我的數據,現在處理它。」

有什麼我錯過了什麼?

更新: 澄清,我們現有的代碼主要是直接調用工廠。因此,要獲得一個新的球對象,你會看到這樣的代碼:

Ball myBall = BallFactory.getObject(); 

很多的工廠都實現以允許新的具體對象類型的運行時間登記 - 一個插件框架。

所以在看一些看起來像DI的初步意見後,我調用的代碼通常不會傳中,球對象,而是將BallFactory。我想這樣做的好處是,類可能更通用,因爲它甚至沒有耦合到它使用的工廠。

+7

您是否將那些工廠作爲參數傳遞給需要的對象?那就是*依賴注入*。 – deceze 2012-08-17 13:57:19

+1

你是在談論工廠類還是靜態工廠方法?無論如何,工廠是你通常如何實現依賴注入。 – 2012-08-17 14:28:50

+0

依賴注入爲您的工作,爲你,不用你。這就是原因(假設你正在使用依賴注入框架)。 – 2012-08-17 15:30:50

回答

2

兩者都不比另一個「更好」;您可以使用依賴注入本身,工廠本身或這些的組合。

您提到的關於工廠的所有3點對於依賴注入同樣有效。請記住,依賴關係可以在任何時間點注入,而不一定由立即的「調用代碼」注入。事實上,這是一個DI框架爲你做的 - 它基本上是一個巨型工廠,它創建你的主應用程序對象併爲你注入所有的依賴關係。

僅當代碼需要能夠在運行時創建依賴項的新實例時,才明確使用工廠。否則,簡單地使用DI框架在應用程序啓動期間注入所有靜態依賴關係就簡單多了。

3

依賴注入有助於在單元測試。它允許你分離和隔離你的類的功能,因爲它的任何依賴可以被注入(因此也可以被模擬)到類中。如果依賴性訪問外部資源(如DB),這特別有用。

最近我看到一篇文章,證明在測試依賴注入的優點。它具體是關於靜態方法,但同樣適用於工廠。

http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/

像@deceze說不過,如果你注入你的工廠你得到兩全其美...

+0

我不是Miško那篇文章的特別大粉絲。它確實提出了一個非常令人信服的觀點,但這是IMO過於極端的做法。靜態方法是非常有用的,你只需要小心*你*使用它們。在閱讀時請記住這一點。 – deceze 2012-08-17 14:37:19

+0

我同意,這是極端的,但它使有效的點,mayb只需要一撮鹽。 – Liam 2012-08-20 08:11:38

+0

您仍然可以將靜態工廠設置爲在運行時爲您的單元測試進行配置。你不需要依賴注入來做到這一點。 – Sam 2014-08-01 06:34:06

1

使用依賴注入與否是有點像使用printf之間在C差異並使用fprintf。呼叫者以必須作出選擇爲代價獲得靈活性。當調用者不需要靈活性時(例如,如果所有程序的輸出都轉換爲stdout,而不是stderr或文件),靈活性是一種純粹的負擔,因爲調用者必須始終傳遞相同的「正確」值。

如果您將依賴注入視爲純粹的負擔,那意味着您的調用者並未真正使用它。

你的觀點1和3都表示,「調用代碼沒有什麼自由來影響發生的事情」,這並不總是一個優勢。測試代碼特別受益於注入依賴關係,但也可能出現其他原因需要靈活性的情況。你是否記錄了printf,或者你是否通過調用注入記錄器的函數來記錄日誌?

第2點歸結爲你如何發展你的API。是的,如果原始設計需要改變,那麼通過使用固定的隱藏依賴關係,您可以屏蔽API以反映這一變化。有時你可以使用新的依賴項的默認值來維護舊的API,並在某處添加一個帶有額外參數的新方法/構造函數。

所有這一切,標準庫在我使用的語言不需要大量的依賴注入。所以你並不孤單,認爲你的API不需要它,但是我懷疑你現在可以從內部獲得更多的利益。例如,你可以在不連接遠程機器的情況下測試你的網絡代碼嗎?如果沒有,請考慮如果可以的話,測試過程的這一部分是否會更容易,更快速並提供更準確的診斷。

2

結合使用依賴注入和抽象工廠通常是有用的 - 但出於兩個獨立的原因。使用(手動)依賴注入的原因是它允許您在單元測試期間注入一個特殊對象。如果您的設計描述調用代碼不負責創建對象(從1-2-3項目符號開始),則提供的依賴項應爲抽象工廠的實例。被注入的對象將使用工廠在需要時創建對象。

假設你使用兩個工廠,生產的步步高容易和硬遊戲的依賴關係(這裏只是一個從屬關係,骰子):

public class EasyGameFactory implements GameFactory 
{ 
    Dice createDice() 
    { 
    return new LuckyDice(); 
    } 
} 

public class NormalGameFactory implements GameFactory 
{ 
    Dice createDice() 
    { 
    return new RandomDice(); 
    } 
} 

爲單元測試的目的,你會真的喜歡既不用骰子實現,所以你寫了一個GameFactory的特殊實現:

public class CustomGameFactory implements GameFactory 
{ 
    private Dice mDice; 

    public CustomGameFactory(Dice dice) 
    { 
    mDice = dice; 
    } 

    Dice createDice() 
    { 
    return mDice; 
    } 
} 

這個工廠不一定是你的生產代碼樹的一部分。您通過測試代碼向工廠提供骰子的特殊實現:

public class TestBackgammon 
{ 
    @Test public void shouldReturnDiceThrown() 
    { 
    SettableDice dice = new SettableDice(); 
    Game game = new GameImpl(new CustomGameFactory(dice)); 

    dice.setDice(new int[] {4, 5}); 
    game.nextTurn(); 
    assertArrayEquals(new int[] {4, 5}, game.diceThrown()); 
    } 
}