dependency injection容器有什么好处?

我明白dependency injection本身的好处。 以Spring为例。 我也了解像AOP这样的其他Spring特性的好处,不同types的帮助器等。我只是想知道XMLconfiguration的好处,例如:

<bean id="Mary" class="foo.bar.Female"> <property name="age" value="23"/> </bean> <bean id="John" class="foo.bar.Male"> <property name="girlfriend" ref="Mary"/> </bean> 

比较普通的老式java代码如:

 Female mary = new Female(); mary.setAge(23); Male john = new Male(); john.setGirlfriend(mary); 

这是更容易debugging,编译时间检查,可以被任何人只知道Java的理解。 那么dependency injection框架的主要目的是什么呢? (或者显示其好处的一段代码)。


更新:
的情况下

 IService myService;// ... public void doSomething() { myService.fetchData(); } 

如果有多个IoC框架,我怎么能猜测我想要注入myService的哪个实现? 如果给定的接口只有一个实现,并且让IoC容器自动决定使用它,那么在第二个实现出现之后它将被中断。 如果有意识地只有一个可能的接口实现,那么你不需要注入它。

看到IoC的一小部分configuration将会非常有趣,它显示了它的优点。 我一直在使用Spring,我不能提供这样的例子。 我可以展示单行显示hibernate,dwr和我使用的其他框架的好处。


更新2:
我意识到IoCconfiguration可以在不重新编译的情况下进行更改。 这真的是个好主意吗? 我可以理解,当有人想要更改数据库凭据而无需重新编译 – 他可能不是开发人员。 在您的实践中,开发人员以外的其他人多久更换一次IoCconfiguration? 我认为对于开发人员来说,没有必要重新编译那个特定的类而不是改变configuration。 而对于非开发人员,您可能希望使他的生活更轻松,并提供一些更简单的configuration文件。


更新3:

接口与其具体实现之间映射的外部configuration

有什么好做的外部? 你不能把所有的代码都放在外部,而你肯定可以 – 只要把它放在ClassName.java.txt文件中,在运行时手动读取和编译,就避免了重新编译。 为什么要避免编译?!

由于您以声明方式提供映射,而不是在程序代码中,所以节省了编码时间

我明白,有时候声明式的方法可以节省时间。 例如,我只声明一次bean属性和一个DB列之间的映射,而hibernate在加载,保存,基于HSQL构buildSQL等时使用这个映射。这就是声明式方法的作用。 在Spring的情况下(在我的例子中),声明有更多的线条,并且与相应的代码具有相同的performance力。 如果有这样一个例子,当这样的声明比代码短 – 我想看看它。

控制原理的反转允许简单的unit testing,因为你可以用真实的实现代替实际的实现(比如将SQL数据库replace为内存中的数据库)

我明白控制好处的倒置(我更喜欢把这里讨论的devise模式称为dependency injection,因为IoC更通用 – 有很多种控制,而且我们只反转其中的一种 – 控制初始化)。 我在问为什么有人为了它而需要其他东西而不是编程语言。 我绝对可以使用代码replace真正的实现。 这段代码将会和configuration一样expression – 它只是用假值初始化字段。

 mary = new FakeFemale(); 

我确实了解DI的好处。 我不明白外部XMLconfiguration与configuration相同的代码相比有哪些优点。 我不认为应该避免编译 – 我每天编译,我还活着。 我认为configurationDI是声明式方法的一个坏例子。 声明可以是有用的,如果被声明一次,而且以不同的方式多次使用,如hibernate cfg,其中bean属性和DB列之间的映射用于保存,加载,build立search查询等。弹簧DIconfiguration可以很容易地转换为configuration代码,就像在这个问题的开始,不是吗? 它只用于bean初始化,不是吗? 这意味着一个声明式的方法不会在这里添加任何东西,是吗?

当我声明hibernate映射时,我只是给hibernate一些信息,它基于它 – 我不告诉它该做什么。 在spring的情况下,我的声明告诉spring完全可以做 – 为什么要申报,为什么不这样做呢?


最后更新:
伙计们,很多答案告诉我有关dependency injection,我知道是好的。 问题是关于DIconfiguration的目的,而不是初始化代码 – 我倾向于认为初始化代码更短,更清晰。 我得到的唯一答案是我的问题是,它避免重新编译,当configuration改变。 我想我应该提出另一个问题,因为这对我来说是一个很大的秘密,为什么在这种情况下应该避免编译。

对我自己来说,使用IoC(并利用外部configuration)的主要原因之一是围绕以下两个方面:

  • testing
  • 生产维护

testing

如果您将testing分为3个场景(这在大规模开发中是相当正常的):

  1. unit testing
  2. 集成testing
  3. 黑匣子testing

你将要做的是为最后两个testing场景(Integration&Black box),不重新编译应用程序的任何部分。

如果你的任何testing场景要求你改变configuration(即:使用另一个组件来模拟一个银行业务集成,或做一个性能负载),这可以很容易地处理(这是由于configuration的DI侧IoC虽然。

另外,如果您的应用程序在多个站点(使用不同的服务器和组件configuration)使用,或者在实时环境中具有变化的configuration,则可以使用testing的后期阶段来validation应用程序是否将处理这些更改。

生产

作为一名开发人员,您不必(也不应该)能够控制生产环境(特别是当您的应用程序分发给多个客户或单独的网站时),这对我来说是使用IoC和外部configuration的真正好处因为基础设施/生产支持可以调整和调整现场环境,而无需返回到开发人员和testing(当他们所要做的只是移动一个组件)。

概要

IoC的外部configuration的主要好处来自于给其他人(非开发人员)configuration应用程序的权力,根据我的经验,这只在有限的一些情况下才有用:

  • 应用程序分发给环境不同的多个站点/客户端。
  • 有限的开发控制/input生产环境和设置。
  • testing场景。

在实践中,我发现,即使在开发一些可以控制环境的东西的时候,它也会运行,随着时间的推移,最好给别人改变configuration的能力:

  • 开发时,你不知道什么时候会改变(应用程序是如此有用,你的公司把它卖给别人)。
  • 我不希望在每次请求可能通过设置和使用良好的configuration模型而进行的轻微更改时,更改代码。

注意:应用程序是指完整的解决scheme(不仅仅是可执行文件),所以应用程序运行所需的所有文件

dependency injection是一种编码风格,其根源在于对象委托通常比对象inheritance更有用的devise模式(即,对象具有 – 关系比对象更有用 – 关系)。 然而,DI工作的另一个要素是创build对象接口。 结合这两种强大的devise模式,软件工程师很快意识到,他们可以创build灵活的松散耦合的代码,从而诞生了dependency injection的概念。 然而,直到在某些高级语言中,DI才真正起飞,才有了反思。 reflection组件是当今大多数DI系统的核心,因为DI的非常酷的方面需要能够以编程方式select对象,并使用外部系统将对象configuration并注入到其他对象中,并独立于对象本身。

语言必须为普通的面向对象编程技术提供良好的支持,并支持对象接口和对象reflection(例如Java和C#)。 虽然可以在C ++系统中使用DI模式构build程序,但由于在语言本身中缺lessreflection支持,因此无法支持应用程序服务器和其他DI平台,从而限制了DI模式的expression能力。

使用DI模式构build的系统的优势:

  1. DI代码更容易重复使用,因为“依赖”function被推广到定义良好的接口中,从而允许将由合适的应用程序平台处理configuration的单独对象随意插入到其他对象中。
  2. DI代码更容易testing。 通过构build实现应用程序逻辑所期望的接口的“模拟”对象,可以在黑盒中testing对象expression的function。
  3. DI代码更加灵活。 这是天生的松散耦合的代码 – 到极点。 这允许程序员select和select对象是如何基于一端的需要的接口和他们expression的接口。
  4. DI对象的外部(Xml)configuration意味着其他人可以在不可预见的方向上自定义您的代码。
  5. 外部configuration也是关注模式的分离,因为对象初始化和对象依赖性pipe理的所有问题都可以由应用服务器来处理。
  6. 请注意,外部configuration不需要使用DI模式,对于简单的互连,小型构build器对象通常是足够的。 两者之间有一个折衷的灵活性。 构build器对象不像外部可见的configuration文件那样灵活。 DI系统的开发人员必须权衡灵活性与便利性之间的优势,注意configuration文件中所述的小规模,细粒度的物体结构控制可能会增加混乱和维护成本。

绝对的DI代码看起来更麻烦,将configuration对象的所有XML文件注入其他对象的缺点似乎很困难。 然而,这是DI系统的重点。 您将代码对象作为一系列configuration设置进行混合和匹配的能力,使您可以使用第三方代码构build复杂的系统,而且代码最less。

问题中提供的例子仅仅涉及适当分解的DI对象库可以提供的expression力的表面。 通过一些练习和很多自律,大多数DI从业者发现他们可以构build具有100%testing应用程序代码覆盖率的系统。 这一点是非常特别的。 这不是对几百行代码的小应用程序的100%testing覆盖率,而是包含数十万行代码的应用程序的100%testing覆盖率。 我无法描述任何其他提供这种可testing性的devise模式。

你是正确的,一个10几行代码的应用比几个对象加上一系列XMLconfiguration文件更容易理解。 但是,如同最强大的devise模式一样,在继续向系统添加新function时,可以find收益。

简而言之,基于大规模DI的应用程序更易于debugging和更易于理解。 虽然Xmlconfiguration不是“编译时间检查”,但是作者知道的所有应用程序服务如果尝试将具有不兼容接口的对象插入到另一个对象中,则会向开发人员提供错误消息。 大多数提供了一个涵盖所有已知对象configuration的“检查”function。 这通过检查待注入的对象A实现对于所有configuration的对象注入的对象B所需的接口来容易且快速地完成。

这是一个加载的问题,但我倾向于同意,大量的XMLconfiguration并不真正有多大的好处。 我喜欢我的应用程序尽可能轻松依赖,包括巨大的框架。

他们在很多时候简化了代码,但是他们也有复杂的开销,使得追踪问题变得相当困难(我已经看到了这样的问题,直接的Java我会更容易处理)。

我想这取决于风格,你喜欢什么?你喜欢飞自己的解决scheme,并有利于了解它内部,或银行现有的解决scheme,可能会certificate是困难的,当configuration不是'刚刚好吗? 这是一个折衷。

但是,XMLconfiguration是我的一个宠儿…我不惜一切代价避免它。

任何时候你可以改变你的代码到数据,你正朝着正确的方向迈出一步。

将数据编码为数据意味着您的代码本身更加通用和可重用。 这也意味着你的数据可以用一种恰如其分的语言来指定。

而且,一个XML文件可以被读入到一个GUI或者其他工具中,并且可以轻易地被操作。 你将如何做到这一点的代码示例?

我经常将大多数人将代码实现为数据的东西,这使得代码更加干净。 我觉得不可思议的是,人们会在代码中而不是数据中创build一个菜单 – 很明显,在代码中执行代码只是因为样板文件而显示错误。

使用DI容器的原因是,您不必在代码中预先configuration十亿个属性,它们只是获取者和设置者。 你真的想用新的X()来硬编码吗? 当然,你可以有一个默认的,但DI容器允许创build非常容易的单例,并允许您专注于代码的细节,而不是初始化它的各种任务。

例如,Spring允许您实现InitializingBean接口并添加一个afterPropertiesSet方法(您也可以指定一个“init-method”来避免将代码耦合到Spring)。 这些方法将允许你确保在你的类实例中指定为字段的任何接口在启动时都被正确地configuration,然后你不再需要对你的getter和setter进行空检查(假设你允许你的单例保持线程安全)。

此外,使用DI容器进行复杂的初始化更容易,而不是自己做。 例如,我协助使用XFire(不是CeltiXFire,我们只使用Java 1.4)。 该应用程序使用了Spring,但不幸的是使用了XFire的services.xmlconfiguration机制。 当一组元素需要声明它具有零个或多个实例而不是一个或多个实例时,我必须重写一些为这个特定服务提供的XFire代码。

Spring bean模式中定义了某些XFire默认值。 所以,如果我们使用Spring来configuration服务,那么bean可能已经被使用了。 相反,发生的是我不得不在services.xml文件中提供一个特定类的实例,而不是使用bean。 为此,我需要提供构造函数并设置在XFireconfiguration中声明的引用。 我需要做出真正的改变,要求我重载一个类。

但是,感谢services.xml文件,我不得不创build四个新的类,根据它们在构造函数的Springconfiguration文件中的默认设置来设置它们的默认值。 如果我们能够使用Springconfiguration,我可以刚刚说:

 <bean id="base" parent="RootXFireBean"> <property name="secondProperty" ref="secondBean" /> </bean> <bean id="secondBean" parent="secondaryXFireBean"> <property name="firstProperty" ref="thirdBean" /> </bean> <bean id="thirdBean" parent="thirdXFireBean"> <property name="secondProperty" ref="myNewBean" /> </bean> <bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" /> 

相反,它看起来更像这样:

 public class TheFirstPointlessClass extends SomeXFireClass { public TheFirstPointlessClass() { setFirstProperty(new TheSecondPointlessClass()); setSecondProperty(new TheThingThatWasHereBefore()); } } public class TheSecondPointlessClass extends YetAnotherXFireClass { public TheSecondPointlessClass() { setFirstProperty(TheThirdPointlessClass()); } } public class TheThirdPointlessClass extends GeeAnotherXFireClass { public TheThirdPointlessClass() { setFirstProperty(new AnotherThingThatWasHereBefore()); setSecondProperty(new WowItsActuallyTheCodeThatChanged()); } } public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout { public WowItsActuallyTheCodeThatChanged() { } public overrideTheMethod(Object[] arguments) { //Do overridden stuff } } 

所以最终的结果是,四个额外的,大多数没有意义的Java类必须被添加到代码库,以实现一个额外的类和一些简单的依赖容器信息实现的影响。 这不是“certificate规则的例外”,这是规则…当代码中的属性已经在DI容器中提供时,处理代码中的怪异现象更加清晰,而您只是简单地改变它们以适应特殊情况,这经常发生。

我有你的答案

在每种方法中显然存在权衡,但外部化的XMLconfiguration文件对于使用构build系统来编译代码而不是IDE的企业开发非常有用。 使用构build系统,您可能需要在代码中注入某些值 – 例如构build版本(每次编译时都可能需要手动更新)。 当你的构build系统从一些版本控制系统中提取代码时,痛苦会更大。 在编译时修改简单的值将需要您更改文件,提交,编译,然后每次更改每次更改。 这些不是您想要提交到版本控制中的更改。

关于构build系统和外部configuration的其他有用的用例:

  • 为不同版本的单个代码库注入样式/样式表
  • 为您的单个代码库注入不同组的dynamic内容(或对它们的引用)
  • 为不同的构build/客户端注入本地化上下文
  • 将web服务URI更改为备份服务器(当主服务器出现故障时)

更新:以上所有示例都是关于不一定需要依赖类的东西。 但是,您可以轻松构build既需要复杂对象又需要自动化的情况 – 例如:

  • 想象一下,你有一个系统,它监视你的网站的stream量。 根据并发用户的数量,它会打开/closures日志logging机制。 也许在机制closures的时候,一个存根对象被放置在它的位置上。
  • 想象一下,你有一个networking会议系统,根据用户的数量,你想要根据参与者的人数来切换P2P的能力

每次在configuration中更改某些内容时,都不需要重新编译代码。 它将简化程序的部署和维护。 例如你可以用一个configuration文件改变一个组件。

你可以插入一个新的执行女朋友。 所以新的女性可以注入而不需要重新编译你的代码。

 <bean id="jane" class="foo.bar.HotFemale"> <property name="age" value="19"/> </bean> <bean id="mary" class="foo.bar.Female"> <property name="age" value="23"/> </bean> <bean id="john" class="foo.bar.Male"> <property name="girlfriend" ref="jane"/> </bean> 

(以上假设女性和HotFyemale实现相同的GirlfFriend界面)

在.NET世界中,大多数IoC框架都提供了XML和代码configuration。

例如,StructureMap和Ninject使用stream畅的接口来configuration容器。 您不再受限于使用XMLconfiguration文件。 在.NET中也存在的Spring,严重依赖XML文件,因为它是他的历史主要configuration接口,但是仍然可以通过编程来configuration容器。

易于将部分configuration组合成最终的完整configuration。

例如,在Web应用程序中,模型,视图和控制器通常在单独的configuration文件中指定。 使用声明式的方法,你可以加载,例如:

   UI-的context.xml
  模型的context.xml
  控制器的context.xml

或者加载一个不同的用户界面和一些额外的控制器:

   AlternateUI-的context.xml
  模型的context.xml
  控制器的context.xml
   ControllerAdditions-的context.xml

在代码中做同样的事情需要一个基础结构来组合部分configuration。 在代码中不是不可能的,但使用IoC框架肯定更容易。

通常,重要的一点是在编写程序之后在更改configuration。 通过代码configuration,你可以隐含地认为改变它的人具有和原作者一样的技能和对源代码的访问。

在生产系统中,将一些设置子集(例如年龄)提取到XML文件是非常实用的,并且允许例如系统pipe理员或支持个人改变该值而不给予它们源代码或其他设置的全部权力 – 或者仅仅将他们与复杂性隔离。

从spring的angular度来看,我可以给你两个答案。

首先,XMLconfiguration不是定义configuration的唯一方法。 大多数事情可以使用注释进行configuration,必须使用XML来完成的事情是configuration代码,而不是反正写代码,就像从库中使用的连接池。 Spring 3包含一个使用Java定义DIconfiguration的方法,类似于您的示例中的手动DIconfiguration。 所以使用Spring并不意味着你必须使用基于XML的configuration文件。

其次,Spring不仅仅是一个DI框架。 它还有许多其他function,包括事务pipe理和AOP。 Spring XMLconfiguration将所有这些概念混合在一起。 通常在同一个configuration文件中,我指定了bean依赖关系,事务设置以及添加了在后台使用AOP实际处理的会话作用域Bean。 我发现XMLconfiguration提供了一个更好的地方来pipe理所有这些function。 我也觉得基于注释的configuration和XMLconfiguration比基于Java的configuration更好。

But I do see your point and there isn't anything wrong with defining the dependency injection configuration in Java. I normally do that myself in unit tests and when I'm working on a project small enough that I haven't added a DI framework. I don't normally specify configuration in Java because to me that's the kind plumbing code that I'm trying to get away from writing when I chose to use Spring. That's a preference though, it doesn't mean that XML configuration is superior to Java based configuration.

Spring also has a properties loader. We use this method to set variables that are dependant on the environment (eg development, testing, acceptance, production, …). This could be for example the queue to listen to.

If there is no reason why the property would change, there is also no reason to configure it in this way.

Your case is very simple and therefore doesn't need an IoC (Inversion of Control) container like Spring. On the other hand, when you "program to interfaces, not implementations" (which is a good practice in OOP), you can have code like this:

 IService myService; // ... public void doSomething() { myService.fetchData(); } 

(note that the type of myService is IService — an interface, not a concrete implementation). Now it can be handy to let your IoC container automatically provide the correct concrete instance of IService during initialization – when you have many interfaces and many implementations, it can be cumbersome to do that by hand. Main benefits of an IoC container (dependency injection framework) are:

  • External configuration of mapping between interfaces and their concrete implementations
  • IoC container handles some tricky issues like resolving complicated dependency graphs, managing component's lifetime etc.
  • You save coding time because you provide mappings declaratively, not in a procedural code
  • Inversion of Control principle allows for easy unit testing because you can replace real implementations with fake ones (like replacing SQL database with an in-memory one)

Initializing in an XML config file will simplify your debugging / adapting work with a client who has your app deployed on their computers. (Because it doesn't require recompilation + binary files replacement)

One of the most appealing reasons is the " Hollywood principle ": don't call us, we'll call you. A component is not required to do the lookups to other components and services itself; instead they are provided to it automatically. In Java, this means that it is no longer necessary to do JNDI lookups inside the component.

It is also lots easier to unit test a component in isolation: instead of giving it an actual implementation of the components it needs, you simply use (possibly auto generated) mocks.