【问题标题】:IOC/DI container and child window creation dilemmaIOC/DI 容器和子窗口创建困境
【发布时间】:2016-09-28 18:12:33
【问题描述】:

我正在尝试使用 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 容器,我所有的父窗口都必须通过工厂,这不是让我的工厂成为新的“服务器定位器”吗?

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

【问题讨论】:

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


【解决方案1】:

您建议的解决方案是朝着正确方向迈出的一大步。这家工厂闻起来不像是定位器,而是一家本地工厂,是它所属领域的一部分。

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

更多细节和代码示例在我的博客条目中

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

【讨论】:

    【解决方案2】:

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

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

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

    通常我认为将 DI 容器用作工厂会更好,因为它提供了更多功能,例如对象生命周期...

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

    【讨论】:

    • 你能提供一些“使用DI容器作为工厂”和“ChildWindowWrapper”的例子
    • @LeY 对不起,如果听起来模棱两可。我想说我宁愿使用 DI 容器来创建对象的新实例,而不是使用工厂模式(方法)。例如,如果类没有实现接口或抽象类,我将使用包装类。 Wrapper 是封装您需要的某些功能的类。而不是直接在代码中使用您的类,您将需要直接使用 Wrapper。 Wrapper 需要实现接口,您将能够注册到您的 DI 容器。
    【解决方案3】:

    使用 DI 意味着您将容器作为服务定位器在某处使用,最好在一行代码中执行一次。这称为“组合根”,在此配置容器并创建对象图的根。

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

    旁注:

    我总是用我自己的类来包装 Container 框架代码,这样我就可以更轻松地切换 DI 框架 - 考虑一下。

    【讨论】:

    • 使用 DI 并不意味着直接使用容器。在整个堆栈中显式地显示依赖关系甚至可以被认为是代码异味。相反,通过组合根和本地工厂的组合,您可以只拥有容器的堆栈顶部依赖项,并在任何地方使用本地工厂,而根本不依赖任何容器。
    • 我完全同意,这正是我所说的——如果你仔细阅读的话——减去我没有提到的当地工厂部分。 (编辑了答案,这样会更清楚)。 @WiktorZychla
    • 请注意,CR 只是故事的一半。您可以在那里配置容器,但是您需要以某种方式将容器向下传递到堆栈中。本地工厂的想法是另一半,使用本地工厂可以消除引用容器的需要。
    • 在 WPF 中,您可以很容易地摆脱这种必要性,方法是滚动您自己的标记扩展或使用一些通常会处理此类麻烦的 MVVM 框架。我确信也有一些适用于 WF 的框架。我认为工厂在某种程度上是服务定位器的体现,也是另一个问题——它们意味着对实例生命周期的控制,在经典的 DI 场景中不应该留给依赖类。 @WiktorZychla
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    相关资源
    最近更新 更多