IOC容器在这里为我做了什么?

本文关键字:什么 在这里 IOC | 更新日期: 2023-09-27 18:05:05

所以我已经完全重构到构造函数注入,现在我有一个引导器类,看起来像这样:

var container = new UnityContainer();
container.RegisterType<Type1, Impl1>();
container.RegisterType<Type2, Impl2>();
container.RegisterType<Type3, Impl3>();
container.RegisterType<Type4, Impl4>();
var type4Impl = container.Resolve((typeof)Type4) as Type4;
type4Impl.Run();

我盯着它看了一秒钟,才意识到Unity并没有为我做任何特别的事情。如果不使用actor符号,上面的代码可以写成:

Type1 type1Impl = Impl1();
Type2 type2Impl = Impl2();
Type3 type3Impl = Impl3(type1Impl, type2Impl);
Type4 type4Impl = Impl4(type1Impl, type3Impl);
type4Impl.Run();

构造函数注入重构是伟大的,真正打开了代码的可测试性。然而,我怀疑Unity在这里是否有用。我意识到我可能以一种有限的方式使用框架(即不在任何地方注入容器,在代码中配置而不是XML,没有利用生命周期管理选项),但我没有看到它在这个例子中实际上是如何帮助的。我读过不止一条评论,认为DI最好只是作为一种模式使用,而不使用容器。这是一个很好的例子吗?这个解决方案还提供了哪些我错过的好处?

IOC容器在这里为我做了什么?

我发现当容器中有许多相互依赖的类型时,DI容器变得很有价值。正是在这一点上,容器的自动连接能力才闪闪发光。

如果您发现在取出对象时引用的是容器,那么您确实遵循了服务定位器模式。

在某种程度上你是对的。控制反转根本不需要使用IoC容器。如果你的对象图足够小,足够方便,可以在某种引导代码中一次创建,那也是控制反转

但是在更复杂的场景下,使用IoC工具可以简化对象的创建。使用IoC工具,您可以管理对象生命周期,从不同的配置组合对象图,或者在编译时不知道整个图的情况下,轻松地推迟对象创建等等。

没有通解。一切都取决于你的具体需求。对于类很少的简单项目,使用IoC可能会带来更多麻烦而不是帮助。对于一个大项目,我甚至无法想象引导代码需要是什么样子。

请参阅我在这里的帖子,查看对这个问题的广泛回应。

这里的大多数其他答案都是正确的,并且说的几乎是相同的事情。我要补充的是,大多数IoC容器允许您将类型自动绑定到它们自己,或者按照约定使用绑定。如果你设置Unity来做这件事,那么你就可以完全摆脱所有的绑定代码。

区别在于你做的是依赖注入,而不是Unity做依赖注入。在您的示例中,您必须知道需要创建哪些类型,将代码与这些类型耦合。现在,您需要在代码中知道,只要需要Type1,就应该创建Impl1。

这里有一个简单的代码说明其他人所说的内容(尽管有一些自由,属性注入而不是构造函数注入,并且假设您已经注册了类型,等等)。

public interface IFoo { }
public interface IBar { IFoo FooImpl { get; set; } }
public interface IBaz { IBar BarImpl { get; set; } }
public interface IBat { IBaz BazImpl { get; set; } }

随着对象图的增长和依赖关系的嵌套,您将不得不提供整个树:

var bat = new Bat{
        BazImpl = new BazImpl() {
                    BarImpl = new BarImpl() {
                        FooImpl = new FooImpl()
                        }
                    }
                };

然而,如果你正确地使用容器,所有的解析都是基于你注册的内容:

var bat = container.Resolve<IBat>()

与其他答案可能已经说明的很相似,执行依赖注入并不需要IoC容器。它只是提供了自动化的依赖注入。如果您没有从自动化中获得太多好处,那么就不要太担心容器,特别是在应用程序的入口点,也就是注入初始对象的地方。

但是,IoC可以使一些事情变得更容易:

  • 延迟初始化。Autofac和其他一些(不确定Unity)可以检测到一个构造函数,它接受一个Func<IMyDependency>,并且给定一个IDependency的注册,将自动生成适当的工厂方法。这减少了DI系统中通常需要的前端加载,在DI系统中,许多大对象(如存储库)必须初始化并传递给顶级对象。
  • Sub-dependency隐藏。假设类A需要实例化类B,类B需要C,但A不应该知道C,甚至创建A的类Z也不可能知道C。这就是创建ioc的原因;把A、B和C扔进容器,摇一摇,分解出一个完全水合的B给A,或者一个可以注入A(自动)的工厂方法,A可以用它来创建它想要的所有B引用。
  • 简单的"独立"。与创建和使用静态单例不同,IoC可以被告知创建并返回任何已注册依赖项的一个且仅一个实例,无论该依赖项被请求了多少次。这允许开发人员将任何普通实例类转换为容器中使用的单例类,而不需要更改类本身的代码。

您的示例非常简单,并且无需使用DI框架即可轻松管理对象图。如果这确实是需要的范围,那么手动DI就可以了。

使用DI框架的价值随着依赖关系图变得更加复杂而迅速上升。