IoC.Resolve与构造函数注入

时间:2010-02-01 13:12:10

标签: .net inversion-of-control ioc-container

我听到很多人说使用IoC.Resolve()是一种不好的做法,但我从来没有听说过一个很好的理由(如果它只是关于测试而不是你可以嘲笑容器,那么你就是完成)。

现在使用Resolve而不是Constructor Injection的优点是您不需要创建 在构造函数中有5个参数的类,以及每当你要创建的实例时 那个课你不需要提供任何东西

8 个答案:

答案 0 :(得分:49)

IoC.Resolve<>Service Locator模式的一个示例。该模式强加了一些构造函数注入没有的限制:

  • 由于静态调用
  • ,对象不能具有比应用程序域更细粒度的上下文
  • 对象决定要解析的依赖项的版本。某个类的所有实例都将获得相同的依赖配置。
  • 将代码耦合到容器的诱惑很高,例如,而不是创建一个有意图的工厂。
  • 单元测试需要容器配置,其中只能创建和使用类。 (由于上面的第二个问题,当你想测试同一个类的多个配置时,这尤其麻烦。)
  • 无法从其公共API推断应用程序的结构。 (构造函数参数是良好的东西。它们不是你应该感到需要解决的问题。)

在我看来,这些限制将服务定位器模式置于大球和依赖注入之间的中间位置:如果必须使用它,则非常有用,但到目前为止并非最佳选择。

答案 1 :(得分:26)

如果您创建具有5个依赖项的类,则会遇到IoC.Resolve以外的问题。

拉动依赖项(而不是通过构造函数推送它们)完全忽略了使用IoC框架的重点。您想要反转依赖项。没有你的课程依赖于IoC框架,但反过来。

如果您在某些情况下不需要所有依赖项,那么您可能应该拆分您的类,或者通过使它们成为属性依赖项来使某些依赖项成为可选项。


您的课程取决于容器。除非你提供一个,否则它们将无法工作。无论是真的还是假的都无所谓。它们通过静态依赖性固有地绑定到容器。这会给你额外的工作带来任何你的课程。任何时候你想使用你的类,你需要用它们拖动容器。没有好处!服务定位器只是一切的全局包,这可能违背了面向对象编程的所有原则。

答案 2 :(得分:7)

Ioc.Resolve本质上是服务定位器模式。它有它的位置,但并不理想。从体系结构的角度来看,构造函数注入是首选,因为依赖关系更明确,而SL隐藏了类中的依赖关系。这降低了可测试性,并使该过程变得更加复杂。

如果可以的话,我建议你阅读my recent series关于减少代码耦合的技术,包括SL,DI和IoC。

答案 3 :(得分:3)

一个优点是,使用构造函数注入时,所有类依赖项都是预先可见的。

使用.Resolve,您必须阅读代码才能确定依赖关系。

答案 4 :(得分:1)

我必须指出跳过构造函数注入并使用静态注入并不一定是邪恶的。这有很好的应用,最具体的例子是在工厂模式实现中使用它。

public static class ValidationFactory
{
    public static Result Validate<T>(T obj)
    {
        try
        {
            var validator = ObjectFactory.GetInstance<IValidator<T>>();
            return validator.Validate(obj);
        }
        catch (Exception ex)
        {
            var result = ex.ToResult();
            ...
            return result;
        }
    }    
}

我在StructureMap中使用它来处理我的验证层。

编辑:我直接使用容器的另一个例子是让你的一些域对象成为单例,而不是让它们成为静态类,并引入静态类所做的所有诡计。

在我的一些观点中,我连接了一些像这样的实体。通常我会给一个带有Description属性的Enum给我3个值选择但是在这种情况下第3个也需要是一个字符串而不是int所以我创建了一个包含这3个属性的接口并继承了所有的域对象它。然后我让我的容器扫描我的程序集并自动注册所有这些然后将它们拉出来我只有

SomeObject ISomeView.GetMyObject
{
    get { return new SomeObject { EmpoweredEnumType = 
            ObjectFactory.GetNamedInstance<IEmpEnum>("TheObjectName");
        }
}

答案 5 :(得分:1)

由于这个问题值得商榷,我不会说&#34;使用这个或那个&#34;

看起来使用Service Locator并不是一件好事,如果你可以依赖它(我们通常会在某些DI框架上做)。使用DI,我们可以轻松地更改框架,使用Service Locator,我们可以创建与框架的SL部分的耦合。

关于Bryan Watts的回答,当你稍后阅读时 Service Locator vs Dependency Injection

  

...使用[constructor]注入没有显式请求,服务出现在应用程序类中 - 因此控制反转

     

控制反转是框架的一个共同特征,但它需要付出代价。当您尝试调试时,它往往很难理解并导致问题。 总的来说,除非我需要,否则我宁愿避免它。这并不是说它是一件坏事,只是因为我觉得它需要通过更直接的方式证明自己是正确的。替代品。

然后,如果你稍后阅读是实际使用构造函数注入的另一个理由(控制反转)。

我的观点是,在小型项目中使用SL是可以的,因为主要的不是在我们的自定义开发类之间建立耦合。

使用StructureMap exmaple这应该是可以接受的:

public class Demo
{
    private ISomething something = ObjectFactory.GetInstance<ISomething>();
    private IFoo foo = ObjectFactory.GetInstance<IFoo>();
}

是的,代码取决于SM Frx,但是你多久更换一次DI Frx?

对于单元测试,可以设置模拟

public class SomeTestClass
{
    public SomeTest()
    {
        ObjectFactory.Inject<ISomething>(SomeMockGoesHere);
        ObjectFactory.Inject<IFoo>(SomeMockGoesHere);

        Demo demo = new Demo() //will use mocks now
    }
}
  

使用Resolve而不是Constructor Injection的优点是您不需要在构造函数中创建具有5个参数的类

但最终可能会制造更多&#34;管道&#34;用于单元测试。

答案 6 :(得分:0)

我想说这是一个疯狂的参数注入量。

争取一个参数,最多可能是2个,这几乎是所有场景,当然应该是一个接口。除此之外,我闻到一只老鼠(设计缺陷)。

答案 7 :(得分:0)

  

您不需要在构造函数中创建具有5个参数的类,并且每当您要创建该类的实例时,您都不需要为其提供任何内容

几点:

  • 如果您正在使用DI容器,则应该为您创建该类的实例。在这种情况下,您不必为生产使用提供任何东西。对于测试,您必须通过构造函数向它提供依赖项,但是:
  • 如果这个类依赖于(以某种方式使用)那些你正在谈论提供给构造函数的东西(如果没有那么你就不会提供它们)你将不得不提供它们一个方式或其他。在测试时(这是你应该自己调用构造函数的唯一时间)你可以通过构造函数将这些东西传递给它,或者你可以编写代码来设置容器并向它添加这5个东西,以便{{{ 1}}被调用,它们实际上就在那里。我会说,将它们传递给构造函数要容易得多。

即使您没有通过类的API(在这种情况下是构造函数),也会存在依赖关系。但是,理解并测试试图隐藏其依赖关系的类会更加困难。