IoC.Resolve与构造函数注入

我听到很多人说使用IoC.Resolve()是不好的做法,但是我从来没有听说过一个很好的理由(如果是testing而不是模拟容器,那么你就完成了)。

现在使用Resolve而不是Constructor Injection的优点是你不需要在构造函数中创build具有5个参数的类,并且每当你要创build该类的实例时,你都不需要提供它什么

IoC.Resolve<>是Service Locator模式的一个例子。 该模式强加了一些构造函数注入不允许的限制:

  • 由于静态调用,对象可能没有比应用程序域更精细的上下文
  • 对象决定要parsing哪个版本的依赖关系。 某个类的所有实例将获得相同的依赖configuration。
  • 将代码耦合到容器的诱惑是很高的,例如,而不是创build一个意图揭示工厂。
  • unit testing需要容器configuration,其中类可以创build和使用,否则。 (由于上面第二个问题,当你想testing同一个类的多个configuration时,这是特别麻烦的。)
  • 应用程序的结构不能从其公共API推断出来。 (构造器参数是一件好事,它们不是你需要解决的问题。)

在我看来,这些限制将服务定位器模式放到了一大块泥浆和dependency injection之间的中间地带:如果您必须使用它,那么这是非常有用的,但是这并不是最好的select。

如果您创build具有5个依赖关系的类,则除了IoC.Resolve之外,还有其他问题。

拉取依赖关系(而不是通过构造函数推送它们)完全忽略了使用IoC框架的要点。 你想颠倒依赖关系。 没有你的类依赖于IoC框架,但相反。

如果你在某些情况下不需要所有的依赖关系,也许你应该拆分你的类,或者让一些依赖关系通过使它们成为属性依赖关系而成为可选的。


你的课程取决于容器。 他们不会工作,除非你提供一个。 不pipe是真的还是假的都没有关系。 它们通过静态依赖关系固有地绑定到容器上。 这为你增加了额外的工作来处理你的课程。 任何时候你想使用你的课,你需要拖动它们的容器。 没有任何好处! 服务定位器只是所有事物的一个全局包,这可能违反了面向对象编程的所有原则。

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

如果可以的话,我build议你阅读我最近的关于减less代码耦合的技术系列 ,其中包括SL,DI和IoC。

一个优点是,在构造器注入中,所有的类依赖关系都是可见的。

使用.Resolve你必须阅读代码只是为了确定依赖关系。

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

 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来处理我的validation层。

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

在我的一些观点中,我连接了一些这样的实体。 通常我会给我一个Enum的Description属性给我3个值的select,但在这种情况下,第三个需要是一个string也不是一个int,所以我创build了一个接口与这3个属性,并inheritance所有的域对象它。 然后我有我的容器扫描我的程序集,并自动注册所有这些,然后把它们拉出来,我只是有

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

由于这个问题是有争议的,我不会说“使用这个或那个”

看起来像使用服务定位器是不是一件坏事,如果你可以依赖它(我们通常做一些DI框架)。 使用DI,我们可以轻松地更改框架,使用Service Locator我们创build耦合到框架的SL部分。

关于Bryan Watts在稍后在Service Locator vs Dependency Injection中读到的答案

… [constructor]注入没有明确的请求,服务出现在应用程序类中,因此控制反转

控制反转是框架的一个共同特征,但这是一个代价。 当您尝试debugging时,它往往难以理解并导致问题。 所以总的来说,我宁愿避免它,除非我需要它。 这并不是说这是一件坏事,只是我认为它需要更直接的select来certificate自己的合理性。

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

我的观点是,在小型项目中使用SL是可以的,因为最重要的不是创build我们定制的开发类之间的耦合。

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

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

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

而对于unit testing模拟可以设置

 public class SomeTestClass { public SomeTest() { ObjectFactory.Inject<ISomething>(SomeMockGoesHere); ObjectFactory.Inject<IFoo>(SomeMockGoesHere); Demo demo = new Demo() //will use mocks now } } 

使用Resolve而不是Constructor Injection的好处在于,您不需要在构造函数中创build具有5个参数的类

但是你最终可能会为unit testing做更多的“pipe道工”。

我会说这是一个疯狂的参数被注入。

争取一个参数,最多2个,几乎在所有情况下都可能,当然应该是一个接口。 除此之外,我闻到一只老鼠(devise缺陷)。

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

几点:

  • 如果您使用的是DI容器,则应该为您创build该类的实例。 在这种情况下,您不必为了生产而自己提供任何东西。 为了testing,你必须通过构造函数提供依赖关系,但是:
  • 如果这个类依赖于(以某种方式使用)你正在谈论的提供给构造函数的5件事(如果不提供它们,你将不会提供它们),你将不得不单独提供它们,其他。 当testing(这是唯一一次你必须自己调用构造函数的时候),你可以通过构造函数把这些东西传递给它,或者你可以编写代码来设置容器,并将这5件事情添加到它,以便当IoC.Resolve()被调用,他们实际上在那里。 把它们传递给构造函数要容易得多,我想说。

即使你没有通过类的API(在这种情况下它是构造函数)来表示依赖关系也是存在的。 然而,理解和testing这类试图隐藏它们的依赖关系将会困难得多。