我已经使用依赖注入(DI)有一段时间了,在构造函数、属性或方法中进行注入。我从未觉得有必要使用反转控制(IoC)容器。然而,我读得越多,我就越感到来自社区的使用IoC容器的压力。
我使用过StructureMap、NInject、Unity和Funq等。net容器。我仍然没有看到IoC容器将如何受益/改进我的代码。
我也害怕在工作中开始使用容器,因为我的许多同事会看到他们不理解的代码。他们中的许多人可能不愿意学习新技术。
请说服我,我需要使用IoC容器。当我在工作中与其他开发人员交谈时,我将使用这些论点。
想必没有人强迫您使用依赖注入容器框架。您已经使用DI来解耦类并改进可测试性,因此您将获得许多好处。简而言之,你喜欢简单,这通常是件好事。
如果您的系统达到了一个复杂的水平,手动DI变成了一件苦差事(也就是说,增加了维护),请将其与DI容器框架的团队学习曲线进行权衡。
如果您需要对依赖生命周期管理进行更多的控制(也就是说,如果您觉得需要实现单例模式),请查看DI容器。
如果使用DI容器,请只使用需要的特性。跳过XML配置文件,在代码中配置即可。坚持构造函数注入。Unity或StructureMap的基本内容可以压缩到几个页面中。
Mark Seemann写了一篇很棒的博文:什么时候使用DI容器
As you continue to decouple your classes and invert your dependencies, the classes continue to stay small and the "dependency graph" continues to grow in size. (This isn't bad.) Using basic features of an IoC container makes wiring up all these objects trivial, but doing it manually can get very burdensome. For example, what if I want to create a new instance of "Foo" but it needs a "Bar". And a "Bar" needs an "A", "B", and "C". And each of those need 3 other things, etc etc. (yes, I can't come up with good fake names :) ).
使用IoC容器为您构建对象图可以大大降低复杂性,并将其推到一次性配置中。我只需说“给我创建一个Foo”,它就会计算出构建一个Foo需要什么。
有些人将IoC容器用于更多的基础设施,这对于高级场景来说是很好的,但在这些情况下,我同意它会使新开发人员难以阅读和调试代码。
我认为IoC的大部分价值都是通过使用DI获得的。既然你已经这样做了,剩下的好处是递增的。
你得到的值将取决于你正在处理的应用程序的类型:
For multi-tenant, the IoC container can take care of some of the infrastructure code for loading different client resources. When you need a component that is client specific, use a custom selector to do handle the logic and don't worry about it from your client code. You can certainly build this yourself but here's an example of how an IoC can help.
With many points of extensibility, the IoC can be used to load components from configuration. This is a common thing to build but tools are provided by the container.
If you want to use AOP for some cross-cutting concerns, the IoC provides hooks to intercept method invocations. This is less commonly done ad-hoc on projects but the IoC makes it easier.
我以前写过这样的功能,但如果我现在需要这些功能中的任何一个,我宁愿使用一个预先构建并经过测试的工具,如果它适合我的架构的话。
正如其他人所提到的,您还可以集中配置希望使用的类。虽然这可能是一件好事,但代价是误导和复杂化。大多数应用程序的核心组件都没有被替换,因此很难做出取舍。
我使用IoC容器,并欣赏其功能,但不得不承认我注意到了权衡:我的代码在类级别变得更加清晰,而在应用程序级别变得不那么清晰(即可视化控制流)。
I'm a recovering IOC addict. I'm finding it hard to justify using IOC for DI in most cases these days. IOC containers sacrifice compile time checking and supposedly in return give you "easy" setup, complex lifetime management and on the fly discovering of dependencies at run time. I find the loss of compile time checking and resulting run time magic/exceptions, is not worth the bells and whistles in the vast majority of cases. In large enterprise applications they can make it very difficult to follow what is going on.
我不相信集中化的说法,因为你可以通过为你的应用程序使用一个抽象工厂,并虔诚地将对象创建推迟到抽象工厂,即进行适当的DI,来非常容易地集中静态设置。
为什么不像这样做静态无魔法DI:
interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }
class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }
interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }
class Root : IRoot
{
public IMiddle Middle { get; set; }
public Root(IMiddle middle)
{
Middle = middle;
}
}
class Middle : IMiddle
{
public ILeaf Leaf { get; set; }
public Middle(ILeaf leaf)
{
Leaf = leaf;
}
}
class Leaf : ILeaf
{
IServiceA ServiceA { get; set; }
IServiceB ServiceB { get; set; }
public Leaf(IServiceA serviceA, IServiceB serviceB)
{
ServiceA = serviceA;
ServiceB = serviceB;
}
}
interface IApplicationFactory
{
IRoot CreateRoot();
}
abstract class ApplicationAbstractFactory : IApplicationFactory
{
protected abstract IServiceA ServiceA { get; }
protected abstract IServiceB ServiceB { get; }
protected IMiddle CreateMiddle()
{
return new Middle(CreateLeaf());
}
protected ILeaf CreateLeaf()
{
return new Leaf(ServiceA,ServiceB);
}
public IRoot CreateRoot()
{
return new Root(CreateMiddle());
}
}
class ProductionApplication : ApplicationAbstractFactory
{
protected override IServiceA ServiceA
{
get { return new ServiceA(); }
}
protected override IServiceB ServiceB
{
get { return new ServiceB(); }
}
}
class FunctionalTestsApplication : ApplicationAbstractFactory
{
protected override IServiceA ServiceA
{
get { return new StubServiceA(); }
}
protected override IServiceB ServiceB
{
get { return new StubServiceB(); }
}
}
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var factory = new ProductionApplication();
var root = factory.CreateRoot();
}
}
//[TestFixture]
class FunctionalTests
{
//[Test]
public void Test()
{
var factory = new FunctionalTestsApplication();
var root = factory.CreateRoot();
}
}
}
容器配置是抽象工厂实现,注册是抽象成员的实现。
如果您需要一个新的单例依赖项,只需向抽象工厂添加另一个抽象属性即可。如果你需要一个瞬态依赖,只需添加另一个方法并将其作为Func<>注入即可。
优点:
所有的设置和对象创建配置都是集中的。
配置只是代码
编译时检查使其易于维护,因为您不会忘记更新注册。
没有运行时反射魔法
我建议持怀疑态度的人尝试下一个新项目,诚实地问问自己什么时候需要这种容器。稍后很容易引入IOC容器,因为您只是用IOC容器配置模块替换了一个工厂实现。