我听到很多人说使用IoC.Resolve()是一种不好的实践,但我从未听到过一个好的原因(如果这与测试有关,那么你只需要模拟容器即可)。
现在使用Resolve而不是构造函数注入的优点是您不需要创建具有5个构造函数参数的类,每当您要创建该类的实例时,您都不需要为其提供任何内容。
我听到很多人说使用IoC.Resolve()是一种不好的实践,但我从未听到过一个好的原因(如果这与测试有关,那么你只需要模拟容器即可)。
现在使用Resolve而不是构造函数注入的优点是您不需要创建具有5个构造函数参数的类,每当您要创建该类的实例时,您都不需要为其提供任何内容。
IoC.Resolve<>
是 服务定位器 模式的一个示例。该模式对于构造函数注入没有的一些限制:
在我看来,这些限制将服务定位器模式归为大型代码堆积和依赖注入之间的中间地带:如果必须使用它,则很有用,但绝不是最佳选择。
.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来处理我的验证层。
编辑:我另外一个使用容器的例子是使一些域对象成为单例,而不是将它们变成静态类并引入所有静态类所具有的怪异性。
在我的一些视图中,我像这样连接一些实体。通常我会使用带有Description属性的枚举来给我3个值选项,但在这种情况下,第三个选项也需要是字符串而不是整数,因此我创建了一个接口,其中包含这3个属性,并从中继承所有域对象。然后我让容器扫描我的程序集并自动注册所有对象,然后要提取它们,我只需
SomeObject ISomeView.GetMyObject
{
get { return new SomeObject { EmpoweredEnumType =
ObjectFactory.GetNamedInstance<IEmpEnum>("TheObjectName");
}
}
由于这个问题有争议,我不会说“使用这个或那个”
如果你可以依赖Service Locator并且我们通常在某些DI框架上这样做,那么使用Service Locator似乎并不是一件坏事。通过DI,我们可以轻松更改框架,而使用Service Locator则会创建与框架的SL部分的耦合。
关于Bryan Watts的回答,当你稍后阅读 Service Locator vs Dependency Injection时
......[构造函数]注入没有明确的请求,服务出现在应用程序类中-因此是控制反转。
控制反转是框架的常见特征,但它是需要付出代价的。它往往很难理解,并且在尝试调试时会导致问题。所以总的来说,除非需要,否则我更喜欢避免使用它。这并不是说它是一件坏事,只是我认为它需要证明自己比更简单的替代方案更好。
然后,如果您稍后阅读,就会发现另一个使用构造函数注入(控制反转)的正当理由。
我认为在小型项目中使用SL是可以的,关键是不能在我们自定义的类之间创建耦合。
以StructureMap为例,这应该是可接受的:
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
}
}
我会说这是注入的参数数量太多了。
应该努力实现只有一个参数,最多两个参数,而且在几乎所有情况下都应该是可能的,并且当然应该是一个接口。如果超过这个数量,那么我就会嗅到设计缺陷的味道。
IoC.Resolve()
时,它们实际上存在。我认为通过构造函数传递它们会更容易。即使您不通过类的API(在这种情况下是构造函数)明确表明,依赖关系仍将存在。然而,试图隐藏其依赖关系的类将更难理解和测试。
IEmailService
,它有两个实现:一个发送实时电子邮件,另一个仅发送到本地服务器。如果类Foo
通过IoC.Resolve<>
请求IEmailService
,它只能选择其中一个 - 该决策不考虑将使用哪个上下文来解决IEmailService
配置的因素。所有Foo
的实例都将获取相同的IEmailService
配置。 - Bryan Watts