2013-03-14 79 views
2

我是依賴注入的新手,並且一直在StackOverflow和其他地方對它進行一些閱讀。在實踐中,我無法正確使用它。依賴注入中的一些概念

爲了解釋這個問題,下面是一個我不確定如何使用DI的基本情況:假設我有一些對象將在幾個不同的類中使用。但是,爲了使這個對象可用,它需要一些我在啓動時沒有的參數。

我可以看到使用DI來做這件事的一個可以想象的方式是創建一個空對象的實例,使用必要的參數初始化它的方法,以及是否初始化它的標誌。

對我來說,這感覺就像一個黑客,因爲該對象應該不存在,我只是傳遞一個容器,等待負責的代碼初始化它。這是如何完成的,還是我錯過了這個觀點?

回答

2

當開始使用DI時,這確實是一件相當困難的事情,而且這也是不容易解釋的。

您的想法是:創建一個「空白」對象,稍後通過方法初始化可能是次優解決方案是正確的 - 對象應該能夠隨時完成其工作; Initialize()方法是Mark Seemann在他的書.NET中的依賴注入中稱爲「時間耦合」的方法。這是一種反模式,它使代碼使用依賴於該對象內部工作的對象,從而中斷封裝。

的問題是,當需要的信息可用,什麼是「負責任的代碼來初始化它」,並且它得到從信息 - 而且它是如何獲取到對象的訪問進行初始化。理想情況下,這個初始化代碼本身將被注入到你的對象中,並且每當你的對象的方法/屬性被訪問時,它都會請求從其他依賴項進行初始化。

另外,如果IsInitialized標誌返回false,會發生什麼情況?這仍然是一個有效的程序狀態?一般來說,作爲依賴注入對象圖中的一個對象,我應該知道所有關於創建的「配置」數據,或者知道某人可以給我(某人是另一個注入爲依賴的對象) 。

它可以幫助,如果你能提供有關他們需要來自什麼樣的參數對象的需求和地方更詳細一點。

編輯

什麼你描述你的評論是非常正是我有這種問題的第一次相遇;當時我在某處發佈了一個問題,我當時發佈了這個問題。

重要的是要建立單獨的類(通常情況下,也有例外,但那些是什麼是經驗的問題)以這樣一種方式,你承擔一切類需要存在。當程序運行時,需要有其他類,然後確保假設不會失敗。

setter注入的東西我一般儘量不要有避免所述時間耦合;根據Mark Seemann的說法,通常只有當你已經有一個良好的默認設置時,通過setter才能覆蓋setter注入。但是,在這種情況下,如果沒有該依賴關係,該對象將無法正常工作。

這可能不是最優雅的方式(我通常有奢侈的方式將DI應用於相當封閉的僅用於代碼的環境中,而無需擔心UI),但它可以工作(有點 - 它編譯,但仍是僞代碼):

public class MainForm 
{ 
    private readonly IDataManager _dataManager; 
    private readonly IConnectionProvider _connectionProvider; 
    private readonly IConnectionReceiver _connectionReceiver; 

    public MainForm(IDataManager dataManager, IConnectionProvider connectionProvider, IConnectionReceiver connectionReceiver) 
    { 
     this._dataManager = dataManager; 
     this._connectionProvider = connectionProvider; 
     this._connectionReceiver = connectionReceiver; 
    } 

    public void btnConnect_Click() 
    { 
     IConnection connection = this._connectionProvider.GetConnection(); 

     if (null != connection) 
     { 
      this._connectionReceiver.SetConnection(connection); 

      this.SetFormControlsEnabled(true); 
     } 
    } 

    private void SetFormControlsEnabled(bool doEnable) 
    { 
    } 
} 

public interface IConnectionProvider 
{ 
    IConnection GetConnection(); 
} 

public interface IConnectionReceiver 
{ 
    void SetConnection(IConnection connection); 
} 

public interface IConnection 
{ 
    IConnectionWebService ConnectionWebService { get; } 
} 

public class ConnectionBridge : IConnection, IConnectionReceiver 
{ 
    private IConnection _connection; 

    #region IConnectionReceiver Members 

    public void SetConnection(IConnection connection) 
    { 
     this._connection = connection; 
    } 

    #endregion IConnectionReceiver Members 

    #region IConnection Members 

    public IConnectionWebService ConnectionWebService 
    { 
     get { return this._connection.ConnectionWebService; } 
    } 

    #endregion 
} 

public interface IConnectionWebService {} 

public interface IDataManager { } 

public class DataManager : IDataManager 
{ 
    public DataManager(IConnection connection) 
    { 
    } 
} 

所以,MainForm是保存其結合在一起的事情。它從禁用它的控件開始,因爲它知道他們需要一個工作IDataManager,並且(按慣例)將需要一個連接。當單擊「連接」按鈕時,表單會要求其連接的IConnectionProvider依賴關係。它並不關心連接來自哪裏;連接提供者可能會顯示另一個表單來請求憑據,或者可能只是從文件中讀取它們。

然後窗體知道連接必須傳遞到IConnectionReceiver實例,並且之後可以啓用所有控件。這不是任何DI原理,這只是我們如何定義MainForm的作品。

另一方面,數據管理器擁有從一開始就需要的所有東西 - 一個IConnection實例。這不能做它開始時應該做的事情,但是有其他代碼可以防止這種情況導致問題。

ConnectionBridge既是實際的IConnection實例的修飾器,也是從連接消耗中獲取適配器去耦連接的實例。它通過使用接口隔離原理來實現。

作爲一面的說明,請注意雖然依賴注入是一項重要的技術,但它只是編寫「乾淨代碼」所應遵循的幾個原則之一。最廣爲人知的是SOLID原則(其中DI就是其中之一),但也有其他類似命令查詢分離(CQS)「不要重複自己」(DRY)法的德米特。最重要的是,練習單元測試,正是測試驅動開發(TDD)。這些東西真的會帶來巨大的變化 - 但如果你自己動手處理DI,那麼你已經有了一個好的開始。

+0

其實我覺得我失去了更基本的東西。給你一個更具體的(和基本的)例子:在一個基本的表單應用程序中,假設我有一個窗口,用戶輸入一些憑證。現在,假設我有一個DataManager,它需要Connect的憑證。一旦用戶點擊連接,他將被帶到主窗口,在那裏他可以使用這個DataManager。在這種情況下,DI的流程如何? – user472875 2013-03-14 23:21:21

+0

快速補充:我意識到在這個特定的例子中,還有其他方法可以解決這個問題。但它確實顯示了這個問題:爲了簡單起見,我們沒有添加任何其他圖層,因此我們有2個窗體的類以及DataManager。第二種形式從第一種形式打開,因此第一種形式需要構造函數中的DataManager實例和第二種形式。在顯示它之前,我們可以在第二個窗體上使用setter注入,但是有沒有辦法通過構造函數注入來完成此操作。 – user472875 2013-03-14 23:56:13

+0

感謝您的完整答案。我會實現類似的東西來感受它,以獲得更好的感覺。至於編碼實踐,實際上是因爲我決定嘗試使用非DI代碼的單元測試有多困難。 :) – user472875 2013-03-15 13:27:15

0

我同意GCATNM的說法,我想補充一點,每當我覺得有這樣一個對象時,我就去使用工廠模式變體之一(無論是抽象工廠,靜態工廠等)。我會爲工廠注入該對象的配置信息的來源。正如Marc Seemann所說,我沒有引用:工廠是依賴注入的一個很好的伴侶,你偶爾會需要它們。

+0

解決方案將成爲抽象工廠的可能性很大,但不一定如此。目前信息太少;最後可能會以某種適配器模式結束。 – TeaDrivenDev 2013-03-14 23:12:39