IOC / DI容器和子窗口创建困境

时间:2016-09-28 18:12:34

标签: c# winforms dependency-injection ioc-container

我正在尝试使用IOC / DI容器,但是在创建子窗口时,最佳做法是什么?

我陷入困境的地方是:

public class ParentWindow : Form
{
    public void OpenChildWindow()
    {
        var child = IocContainer.Instance.Resolve<ChildWindow>(); // big issue !!! an-ti server locator pattern
        child.Show();
    }
}

或者

 public class ParentWindow : Form
    {

          private Container _container

          public ParentWindow(Container container) // no, no, you have dependence on container
          {
          }

        public void OpenChildWindow()
        {
            var child = _container.Resolve<ChildWindow>(); 
            child.Show();
        }
    }

我的解决方案

public class ParentWindow : Form
{
    private IFormFactory _factory

    public ParentWindow(IFormFactory factory) // inject from IOC container
    {
    }

    public void OpenChildWindow()
    {
        var child = _factory.CreateChildWindow();
        child.Show();
    }
}

但是根据我的解决方案,我的工厂变成了我自己的IOC容器,我所有的父窗口都必须通过一个工厂,这不是让我的工厂成为新的“服务器定位器”。

还有其他更好的解决方案吗?

3 个答案:

答案 0 :(得分:1)

您建议的解决方案是朝着正确方向迈出的一大步。工厂并不像定位器那样闻起来,相反,它是一个本地工厂,属于它所属的领域。

更进一步的步骤是忘记工厂系列(界面)的想法,并拥有一个具有可插拔实现的具体工厂,内部使用容器(或不使用容器),但为其客户提供单个api 。这样你就可以将工厂的构造函数注入到表单中,而只使用工厂的具体类型。工厂本身在Composition Root中配置。

我博客条目中的更多详细信息和代码示例

http://www.wiktorzychla.com/2016/01/di-factories-and-composition-root.html

答案 1 :(得分:0)

使用DI意味着您将容器作为服务定位器在某处使用,最好是在一行代码中执行一次。这被称为&#34; Composition Root&#34;,其中配置容器并创建对象图的根。

考虑到这一点 - 您示例中最顶层的代码不违反此原则。

旁注:

我一直在做的是用我自己的类包装Container框架代码,以便我可以更容易地切换DI框架 - 考虑它。

答案 2 :(得分:0)

第一个例子:我不喜欢因为你也有依赖,你不能单元测试。它将解析 ChildWindow 的实例,你无法控制(模拟)它。

第二个例子:我不喜欢,因为你使用的是容器而不是接口。

第三个例子:它比以前更好用,有时候我会用这种方式工厂。它可以完全进行单元测试。

通常我认为最好将DI容器用作工厂,因为它提供了更多功能,例如物体寿命......

我从未使用类 ChildWindow 但是如果我无法在构造函数接口中定义,我宁愿实现一些ChildWindowWrapper:IChildWindowWrapper并使用该包装器。它可以简单地进行单元测试,也可以在DI容器中使用。