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

我很清楚这个问题。我所学到的关于解决方案的一切(我学到了很多东西)或多或少是伪装的服务定位器,使一切变得更加复杂。

对我来说最简单最干净的方式是收集主视图模型的构造函数中所需的所有东西,从应用程序的启动位置开始传递这些引用。

相关问题