将依赖注入引入到你的项目当中去,你将发现你的代码显著地变得简洁,易懂和易于测试。
任何有实际意义的程序(颇有可能比Hello World复杂得多)都由不止一个类组成,这些类彼此交互去实现某种业务逻辑。传统方法是,每一个对象都由它本身负责管理与之合做的对象(即它所依赖的对象)的引用。这将致使高度耦合和难以测试的代码。
举例来讲,考虑下面这个Knight类:
java
package com.springinaction.knights; public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; public DamselRescuingKnight() { quest = new RescueDamselQuest(); } public void embarkOnQuest() throws QuestException { quest.embark(); } }
想必你看到了,DamselRescuingKnight在它的构造函数中自行建立了指示RescueDamselQuest。这使得DamselRescuingKnight被紧密地和RescueDamselQuest耦合到了一块儿,并所以极大地限制了这个骑士执行指示的能力。若是一个少女须要救援,这个骑士可以召之即来。可是若是要一条恶龙须要杀掉,或者一个圆桌须要……额……滚起来,那么这个骑士只能袖手旁观了。
更糟糕的是,为这个DamselRescuingKnight编写单元测试将出奇地困难。在这个测试当中,你须要保证当骑士的embarkOnQuest()被调用的时候,指示的embark()也要被调用。可是没有一个简单明了的方式,可以实现这一点。因此不幸地,DamselRescuingKnight将是一个待测试的类。 spring
耦合是个两难的问题(two-headed beast)。一方面,紧密耦合的代码难以测试,难以复用,难以理解,而且典型地表现出“打地鼠”式的bug特性(修复一个bug,致使出现一个新的甚至更多的bug)。另外一方面,必定程度的耦合又是必须的——彻底没有任何耦合的代码将一事无成。为了建功立业,不一样的类必须以适当的方式交互。总而言之,耦合是必须的,可是应当被当心地维护。
另外一方面,经过依赖注入(DI),对象的依赖关系将由系统中负责协调各个对象的第三方组件,在建立对象的时候提供。对象无需去自行建立或管理他们的依赖关系——依赖关系将被注入到须要它们的对象当中。 为了展现这一点,让咱们看一看下面这个BraveKnight,这个骑士不只勇敢,并且能响应任何形式的指示。 函数
package com.springinaction.knights; public class BraveKnight implements Knight { private Quest quest; public BraveKnight(Quest quest) { this.quest = quest; } public void embarkOnQuest() throws QuestException { quest.embark(); } }
正像你看到的那样,不一样于以前的DamselRescuingKnight,BraveKnight没有自行建立指示,而是在构造的时候做为构造器的参数传入指示。这是依赖注入的形式之一,即构造器注入。
更重要的是,他被传入的指示类型是Quest,一个全部指示都必须实现的接口。因此,BraveKnight可以响应RescueDamselQuest,SlayDragonQuest,MakeRoundTableRounderQuest 等传给他的任何其余的Quest的实现。
这里的要点是BraveKnight没有与任何特定的Quest实现发生耦合。对他来讲,被要求响应的指示只要实现了Quest接口,那么具体是哪一类型的指示就可有可无了。这就是依赖注入最大的好处——松耦合。若是一个对象只经过接口(而不是具体实现或初始化的过程)来标明依赖关系,那么这种依赖就可以在对象自己绝不知情的状况下,用不一样的具体实现进行替换。
对依赖进行替换的最经常使用的方法之一,就是在测试的时候用mock实现。你没法充分测试DamselRescuingKnight,由于它是紧耦合的;可是你能够轻松地测试BraveKnight,只需给他一个Quest的mock实现,以下所示:
单元测试
package com.springinaction.knights; import static org.mockito.Mockito.*; import org.junit.Test; public class BraveKnightTest { @Test public void knightShouldEmbarkOnQuest() throws QuestException { Quest mockQuest = mock(Quest.class); BraveKnight knight = new BraveKnight(mockQuest); knight.embarkOnQuest(); verify(mockQuest, times(1)).embark(); } }