當開始使用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,那麼你已經有了一個好的開始。
其實我覺得我失去了更基本的東西。給你一個更具體的(和基本的)例子:在一個基本的表單應用程序中,假設我有一個窗口,用戶輸入一些憑證。現在,假設我有一個DataManager,它需要Connect的憑證。一旦用戶點擊連接,他將被帶到主窗口,在那裏他可以使用這個DataManager。在這種情況下,DI的流程如何? – user472875 2013-03-14 23:21:21
快速補充:我意識到在這個特定的例子中,還有其他方法可以解決這個問題。但它確實顯示了這個問題:爲了簡單起見,我們沒有添加任何其他圖層,因此我們有2個窗體的類以及DataManager。第二種形式從第一種形式打開,因此第一種形式需要構造函數中的DataManager實例和第二種形式。在顯示它之前,我們可以在第二個窗體上使用setter注入,但是有沒有辦法通過構造函數注入來完成此操作。 – user472875 2013-03-14 23:56:13
感謝您的完整答案。我會實現類似的東西來感受它,以獲得更好的感覺。至於編碼實踐,實際上是因爲我決定嘗試使用非DI代碼的單元測試有多困難。 :) – user472875 2013-03-15 13:27:15