【问题标题】:Creating objects with dependencies - dependency injection创建具有依赖关系的对象 - 依赖注入
【发布时间】:2011-12-31 15:09:18
【问题描述】:

假设我们有课:

public class WithDependencies
{
  public WithDependencies(IAmDependencyOne first, IAmDependencyTwo second)
  // ...
}

现在是问题。如何在应用程序中创建 WithDependencies 类的对象? 我知道有很多方法。

new WithDependencies(new DependencyOne(), new DependencyTwo());
new WithDependencies(IoC.Resolve(IDependencyOne), IoC.Resolve(IDependencyTwo());
// register IDependencyOne, IDependencyTwo implementations at app start
IoC.Resolve(WithDependencies);
// register IDependencyOne, IDependencyTwo implementations at app start
// isolate ourselves from concrete IoC Container
MyCustomWithDependenciesFactory.Create();

等等……

你觉得怎么做?

编辑:

因为我没有得到答案或者我不理解它们,所以我会尝试再次询问。假设在某些事件(按钮、计时器等)上,我需要新对象 WithDependencies()。我如何创建它?假设已经配置了 IoC 容器。

【问题讨论】:

  • 我认为对此的任何答案都将从“它取决于”开始......我已经看到了第一种方法的论据,因为它至少使您的依赖关系保持在一种层次结构中,而不是需要一些大型的全球服务提供商。

标签: dependency-injection inversion-of-control ioc-container


【解决方案1】:

这取决于上下文,因此不可能提供一个单一的答案。 概念上你会在Composition Root做这样的事情:

var wd = new WithDependencies(new DependencyOne(), new DependencyTwo());

但是,即使没有 DI 容器,上述代码也并非总是明确的正确答案。在某些情况下,您可能希望在多个消费者之间共享相同的依赖项,如下所示:

var dep1 = new DependencyOne();
var wd = new WithDependencies(dep1, new DependencyTwo());
var another = AnotherWithDependencies(dep1, new DependencyThree());

在其他情况下,您可能想要共享依赖项,在这种情况下,第一个选项更正确。

这只是与生命周期管理有关的 DI 整个维度的一小部分。许多 DI Containers 可以为您解决这个问题,这是一个很好的论据来支持 DI Container 而不是Poor Man's DI。

一旦您开始使用 DI 容器,您应该在解析类型时遵循 Register Resolve Release pattern,让 Auto-wiring 处理实际的组合:

var wd = container.Resolve<WithDependencies>();

以上示例假设容器已经正确配置。

【讨论】:

  • 我真的希望你能回答这个问题。你的书早在出版之前就已经在我的亚马逊愿望清单上了。很快就会读到。我了解终身管理。我想我的问题是调用 container.Resolve 例如按钮单击确定。还是我不应该直接使用容器对象。
  • 在单击按钮时直接使用它是服务定位器反模式,所以不:你不应该这样做:blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
  • 我不明白。以前看过你的文章。我知道将 IServiceLocator 传递给 WithDependencies 对象而不是 IAmDependencyOne,IAmDependencyTwo 是 ServiceLocator 反模式。我想过在 btn 点击时这样做:IoC.Resolve();不将 IoC 传递给 WithDependencies ctor。
【解决方案2】:

如果您需要创建一个具有自己的依赖项的依赖项,您可以 A) 自己做,或 B) 让其他人为您做。选项 A 否定了依赖注入(解耦等)的好处,所以我会说选项 B 是一个更好的起点。现在,我们选择使用工厂模式,无论它采用服务定位器(即IoC.Resolve)、静态工厂还是实例工厂的形式。关键是我们已将该责任委托给外部机构。

静态访问器需要进行许多权衡。 (I went over them in another answer,所以我不会在这里重复它们。)为了避免引入对基础设施或容器的依赖,一个可靠的选择是接受一个工厂来创建WithDependencies,当我们在其他地方需要一个实例时:

public class NeedsWithDependencies
{
    private readonly IWithDependenciesFactory _withDependenciesFactory;

    public NeedsWithDependencies(IWithDependenciesFactory withDependenciesFactory)
    {
        _withDependenciesFactory = withDependenciesFactory;
    }

    public void Foo()
    {
        var withDependencies = _withDependenciesFactory.Create();

        ...Use the instance...
    }
}

接下来,我们可以创建工厂的特定容器实现:

public class WithDependenciesFactory : IWithDependenciesFactory
{
    private readonly IContainer _container;

    public WithDependenciesFactory(IContainer container)
    {
        _container = container
    }

    public WithDependencies Create()
    {
        return _container.Resolve<WithDependencies>();
    }
}

现在NeedsWithDependenciesWithDependencies 如何创建的任何知识完全隔离;它还在其构造函数中公开其所有依赖项,而不是隐藏对静态访问器的依赖项,使其易于重用和测试。

不过,定义所有这些工厂可能会有些麻烦。我喜欢Autofac's factory relationship type,它会检测Func&lt;TDependency&gt;形式的参数并自动注入一个与上面的手工编码工厂相同目的的函数:

public class NeedsWithDependencies
{
    private readonly Func<WithDependencies> _withDependenciesFactory;

    public NeedsWithDependencies(Func<WithDependencies> withDependenciesFactory)
    {
        _withDependenciesFactory = withDependenciesFactory;
    }

    public void Foo()
    {
        var withDependencies = _withDependenciesFactory();

        ...Use the instance...
    }
}

它也适用于运行时参数:

public class NeedsWithDependencies
{
    private readonly Func<int, WithDependencies> _withDependenciesFactory;

    public NeedsWithDependencies(Func<int, WithDependencies> withDependenciesFactory)
    {
        _withDependenciesFactory = withDependenciesFactory;
    }

    public void Foo(int x)
    {
        var withDependencies = _withDependenciesFactory(x);

        ...Use the instance...
    }
}

【讨论】:

  • 我不确定我是否理解第一个示例。我了解您建议创建 WithDependencies 工厂。但是当我们拥有它时,为什么将它传递给 NeedsWithDependencies ctor 而不是将它交给已经创建(使用工厂)的 WithDependencies 对象?这样,NeedsWithDependencies 不了解工厂并且更容易测试(无需在测试中设置模拟)。顺便说一句,WithDependenciesFactory ctor 的名称有错别字。
  • @Peri:我将您的问题解释为关于按需创建WithDependencies 的实例,即等待实例化它们直到您真正需要它们。这就是我对 DI 环境中工厂的看法。不过,我现在意识到,您的问题更多是关于使用 IoC 容器。 @Mark Seemann 关于使用组合根地址的回答很好。
【解决方案3】:

有时我会尝试摆脱工厂,或者至少不直接依赖它们,所以依赖注入(没有工厂)当然很有用。

因此我使用 Google Juice,因为它是一个使用 Java 注释的小框架,您可以快速更改注入/依赖项。看看吧:

http://code.google.com/p/google-guice/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-22
    • 2021-11-19
    • 2016-07-31
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多