《Spring In Action》第三版中文版 Chapter 1 Piece 3

将依赖注入引入到你的项目当中去,你将发现你的代码显著地变得简洁,易懂和易于测试。

任何有实际意义的程序(颇有可能比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(); 
    } 
}
相关文章
相关标签/搜索