已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
当前回答
我想既然每个人都为DI写过文章,让我问几个问题。。
当您有一个DI配置,其中所有实际实现(而不是接口)都将被注入到一个类中(例如控制器的服务),为什么这不是某种硬编码?如果我想在运行时更改对象怎么办?例如,我的配置已经表明,当我实例化MyController时,为FileLogger注入ILogger。但我可能想注入DatabaseLogger。每次我想更改AClass需要的对象时,我都需要查看两个地方——类本身和配置文件。这是如何让生活更轻松的?如果没有注入Aproperty的AClass,是否更难模拟?回到第一个问题。如果使用new object()不好,我们为什么要注入实现而不是接口?我想你们很多人都在说我们实际上是在注入接口,但配置要求您指定接口的实现。。不在运行时。。它在编译时是硬编码的。
这是基于@Adam N发布的答案。
为什么PersonService不再需要担心GroupMembershipService?您刚才提到了GroupMembership依赖于多个东西(对象/财产)。如果PService中需要GMService,您应该将其作为属性。不管你是否注射,你都可以模仿它。我唯一希望它被注入的时候是GMService是否有更具体的子类,这在运行时之前你不会知道。然后您需要注入子类。或者如果您想将其用作单例或原型。老实说,配置文件中的所有内容都是硬编码的,包括它将在编译时为类型(接口)注入的子类。
编辑
Jose Maria Arranz对DI的美好评论
DI通过消除确定依赖方向和编写任何粘合代码的任何需要来提高内聚性。
错误。依赖关系的方向是XML形式或注释,依赖关系是以XML代码和注释的形式编写的。XML和注释是源代码。
DI通过使您的所有组件模块化(即可更换)并具有相互定义良好的接口来减少耦合。
错误。您不需要DI框架来构建基于接口的模块化代码。
关于可替换:使用非常简单的.properties存档和Class.forName,您可以定义哪些类可以更改。如果代码的任何类都可以更改,Java不适合您,请使用脚本语言。顺便说一句:不重新编译就不能更改注释。
在我看来,DI框架只有一个原因:锅炉板减少。有了一个做得好的工厂系统,您可以像首选的DI框架一样做得更好、更可控、更可预测,DI框架可以减少代码(XML和注释也是源代码)。问题是这种锅炉板减少在非常简单的情况下是真实的(每个类一个实例以及类似的情况),有时在现实世界中,选择适当的服务对象不像将类映射到单个对象那样容易。
其他回答
我能想到的最好的类比是手术室中的外科医生和他的助手,在那里,外科医生是主要的人,他的助手在他需要时提供各种手术组件,以便外科医生能够专注于他最擅长的一件事(手术)。如果没有助手,外科医生每次需要时都必须自己取下部件。
简而言之,DI是一种通过向组件提供依赖组件来消除组件获取依赖组件的常见额外责任(负担)的技术。
DI使您更接近单一责任(SR)原则,就像外科医生可以专注于外科手术一样。
何时使用DI:我建议在几乎所有的生产项目(小型/大型)中使用DI,尤其是在不断变化的业务环境中:)
原因:因为您希望代码易于测试、可模拟等,以便快速测试更改并将其推向市场。此外,当你有很多很棒的免费工具/框架来支持你的代码库之旅时,你为什么不这样做呢。
我想既然每个人都为DI写过文章,让我问几个问题。。
当您有一个DI配置,其中所有实际实现(而不是接口)都将被注入到一个类中(例如控制器的服务),为什么这不是某种硬编码?如果我想在运行时更改对象怎么办?例如,我的配置已经表明,当我实例化MyController时,为FileLogger注入ILogger。但我可能想注入DatabaseLogger。每次我想更改AClass需要的对象时,我都需要查看两个地方——类本身和配置文件。这是如何让生活更轻松的?如果没有注入Aproperty的AClass,是否更难模拟?回到第一个问题。如果使用new object()不好,我们为什么要注入实现而不是接口?我想你们很多人都在说我们实际上是在注入接口,但配置要求您指定接口的实现。。不在运行时。。它在编译时是硬编码的。
这是基于@Adam N发布的答案。
为什么PersonService不再需要担心GroupMembershipService?您刚才提到了GroupMembership依赖于多个东西(对象/财产)。如果PService中需要GMService,您应该将其作为属性。不管你是否注射,你都可以模仿它。我唯一希望它被注入的时候是GMService是否有更具体的子类,这在运行时之前你不会知道。然后您需要注入子类。或者如果您想将其用作单例或原型。老实说,配置文件中的所有内容都是硬编码的,包括它将在编译时为类型(接口)注入的子类。
编辑
Jose Maria Arranz对DI的美好评论
DI通过消除确定依赖方向和编写任何粘合代码的任何需要来提高内聚性。
错误。依赖关系的方向是XML形式或注释,依赖关系是以XML代码和注释的形式编写的。XML和注释是源代码。
DI通过使您的所有组件模块化(即可更换)并具有相互定义良好的接口来减少耦合。
错误。您不需要DI框架来构建基于接口的模块化代码。
关于可替换:使用非常简单的.properties存档和Class.forName,您可以定义哪些类可以更改。如果代码的任何类都可以更改,Java不适合您,请使用脚本语言。顺便说一句:不重新编译就不能更改注释。
在我看来,DI框架只有一个原因:锅炉板减少。有了一个做得好的工厂系统,您可以像首选的DI框架一样做得更好、更可控、更可预测,DI框架可以减少代码(XML和注释也是源代码)。问题是这种锅炉板减少在非常简单的情况下是真实的(每个类一个实例以及类似的情况),有时在现实世界中,选择适当的服务对象不像将类映射到单个对象那样容易。
依赖注入是基于框架构建的“控制反转”原则的一种实现。
GoF的“设计模式”中所述的框架是实现主控制流逻辑的类,从而使开发人员能够这样做,这样框架实现了控制原则的反转。
作为一种技术而不是作为类层次结构实现的方法,IoC原则只是依赖注入。
DI主要包括将类实例的映射和对这些实例的类型引用委托给外部“实体”:对象、静态类、组件、框架等。。。
类实例是“依赖项”,调用组件通过引用与类实例的外部绑定是“注入”。
显然,从OOP的角度来看,您可以以多种方式实现该技术,例如,构造函数注入、setter注入、接口注入。
授权第三方执行将引用与对象匹配的任务,这在您希望将需要某些服务的组件与同一服务实现完全分离时非常有用。
这样,在设计组件时,您可以只关注其体系结构和特定逻辑,信任与其他对象协作的接口,而不必担心所使用的对象/服务的任何类型的实现更改,如果您正在使用的同一对象将被完全替换(显然是尊重接口)。
针对5岁儿童的依赖注入。
当你自己去把冰箱里的东西拿出来时,你可能会引起问题。你可能会让门开着,你可能会得到妈妈或爸爸不希望你拥有的东西。你甚至可能在寻找我们甚至没有或已经过期的东西。
你应该做的是陈述一个需要,“我需要午餐时喝点东西”,然后我们会确保你坐下吃饭时有东西。
依赖注入(DI)是依赖反转原理(DIP)实践的一部分,也称为控制反转(IoC)。基本上,你需要做DIP,因为你想让你的代码更加模块化和单元可测试,而不是仅仅一个单片系统。因此,您开始识别可以从类中分离并抽象出来的代码部分。现在抽象的实现需要从类外部注入。通常这可以通过构造函数完成。因此,您创建了一个构造函数,它接受抽象作为参数,这称为依赖注入(通过构造函数)。有关DIP、DI和IoC容器的更多说明,请阅读此处