1

我目前正在製作一個WinForms系統(我知道)在創建表單時有很多Constructor Injection,但如果這些表單/視圖需要打開另一個表單,我發現DI容器也被注入了,所以我們可以在運行時查找所需視圖界面的實現。例如如何避免服務定位符模式?我是不是該?

public partial class MyView : Form, IMyView 
{ 
    private readonly IDIContainer _container; 

    public MyView(IDIContainer container) 
    { 
     InitializeComponent(); 

     _container = container; 
    } 

    public OpenDialogClick(object sender, EventArgs e) 
    { 
     var dialog = container.Resolve<IDialogView>(); 
     dialog.ShowDialog(this); 
    } 
} 

我知道這基本上是使用容器作爲服務定位器。我一再被告知這被認爲是反模式,所以我想避免這種用法。

我大概可以注入視圖這樣構造的一部分:

public partial class MyView : Form, IMyView 
{ 
    private readonly IDialogView _dialog; 

    public MyView(IDialogView dialog) 
    { 
     InitializeComponent(); 

     _dialog = dialog; 
    } 

    public OpenDialogClick(object sender, EventArgs e) 
    { 
     dialog.ShowDialog(this); 
    } 
} 

但如果對話視圖是實例相當昂貴呢?

有人建議我們創建一種內部使用DI容器的表單工廠,但對我來說,這看起來像是簡單地創建另一個服務定位器的包裝。

我知道,在某些時候,東西必須知道如何創建一個IDialogView,所以我想,要麼當複合根目錄中創建(可能不理想,如果有多種形式,它的解決,一些或所有創建都很昂貴),或者複合根本身有解決依賴關係的方法。在這種情況下,組合根必須具有類似於服務定位器的依賴關係?但那麼孩子的形式如何創建這樣的對話框呢?他們會通過事件來調用複合對象來打開這樣的對話框嗎?

我一直遇到的一個特殊問題是容器幾乎不可能輕易地模擬。這部分是讓我思考形式工廠的想法,儘管它只是一個包裝容器的包裝。這是一個明智的理由嗎?

我是不是覺得自己陷入了困境?有沒有簡單的方法通過這個?或者我只是剪了結,找到適合我的東西?

回答

0

當地工廠,滿足與使用容器和在該組合物根設置的實現是服務定位器,它是一個依賴解析器

區別在於:定位器在容器定義附近的某處定義和滿意。在單獨的項目中,要使用定位器,需要對容器基礎結構進行外部引用。這導致項目依賴於外部依賴(定位器)。

另一方面,依賴項解析器對於項目是本地的。它用於滿足鄰域內的依賴關係,但不依賴於任何外部因素。

組合根不應該用於解析實際的特定依賴關係,例如您所關心的依賴關係。相反,組合根應該設置在整個應用程序中使用的所有這些本地依賴解析器的實現。 MVC的構造工廠就是一個很好的例子。另一方面,WebAPI的解析器處理極少數不同的服務,它仍然是一個很好的解決方案 - 它在webapi基礎架構中是本地的,它不依賴於任何東西(而是 - 其他webapi服務依賴於它),它可以是以任何可能的方式實施並在「合成根」中設置。

前一段時間我寫了一個博客條目就

http://www.wiktorzychla.com/2012/12/di-factories-and-composition-root.html

在那裏,你會發現你的問題討論和如何設廠又名解析器的例子。

+0

不幸的是,您的網站因爲「惡意軟件的惡名」而被我的組織阻止:( – adhocgeek

+0

有趣的是,博客如何可能產生「惡意軟件的惡名」?這絕對是您組織方面的一些東西。 –

+0

這絕對是我的公司防火牆,有趣的是,它似乎現在加載......也許這是與您的網站上的廣告有關嗎? – adhocgeek

1

或者我只是剪了結,找到適合我的東西?

工廠類:

 
public interface IDialogFactory { 
    IDialogView CreateNew(); 
} 

// Implementation 
sealed class DialogFactory: IDialogFactory { 
    public IDialogView CreateNew() { 
     return new DialogImpl(); 
    } 
} 

// or singleton... 
sealed class SingleDialogFactory: IDialogFactory { 
    private IDialogView dialog; 
    public IDialogView CreateNew() { 
     if (dialog == null) { 
     dialog = new DialogImpl(); 
     } 
     return dialog; 
    } 
} 

您的代碼:

 
public partial class MyView : Form, IMyView { 
    private readonly IDialogFactory factory; 
    public MyView(IDialogFactory factory) { 
     InitializeComponent(); 
     //assert(factory != null); 
     this.factory = factory; 
    } 

    public OpenDialogClick(object sender, EventArgs e) { 
     using (var dialog = this.factory.CreateNew()) { 
     dialog.ShowDialog(this); 
     } 
    } 
} 

註冊與SimpleInjector

container.RegisterSingle<IDialogFactory, DialogFactory>(); 

或使用單版本

container.RegisterSingle<IDialogFactory, SingleDialogFactory>(); 

container.RegisterSingle<IMyView, MyView>(); 
+0

這不會讓他在任何地方,我猜。現在,要創建'MyView'的實例,他仍然需要容器來解決依賴關係,相反,工廠應該是一個具有可插拔提供者的具體類,'MyView'應該直接使用工廠,不需要注入它。提供者設置在作文根目錄下。 –

+0

對不起,但我不明白爲什麼「要創建實例ces MyView他仍然需要容器來解決依賴性「。 Qithout使用容器,我可以寫:var fac = new SingleFactory(); var myView = new MyView(fac); – o3o

+0

這將違背註冊接口實現的想法。客戶端應該如何知道'IDialogFactory'被註冊到'SingleFactory'? –

0

您絕對不想在應用程序周圍傳遞您的DI容器。您的DI容器應該只是您的Composition Root的一部分。但是,您可以使用組合根中的DI容器的工廠。所以如果Program.cs是你將所有東西連接起來的地方,你可以簡單地在那裏定義那個工廠類。

WinForms不是用DI設計的;表單被生成,因此需要默認的構造函數。這可能會也可能不是問題,具體取決於您使用的DI容器。

我認爲在這種情況下,在WinForms中使用構造函數注入的痛苦大於使用服務定位器時可能遇到的任何缺陷的痛苦。在組合根目錄(Program.cs)中聲明一個靜態方法並不遺憾,該靜態方法將打電話給您的DI容器來解析您的引用。

0

我很清楚這個問題。我所學到的關於解決方案的一切(我學到了很多東西)或多或少是僞裝的服務定位器,使一切變得更加複雜。

對我來說最簡單最乾淨的方式是收集主視圖模型的構造函數中所需的所有東西,從應用程序的啓動位置開始傳遞這些引用。

相關問題