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

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

任何有实际意义的程序(很有可能比Hello World复杂得多)都由不止一个类组成,这些类彼此交互去实现某种业务逻辑。传统方法是,每个对象都由它自己负责管理与之合作的对象(即它所依赖的对象)的引用。这将导致高度耦合和难以测试的代码。

 举例来说,考虑下面这个Knight类:

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将是一个待测试的类。 

耦合是个两难的问题(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(); 
    } 
}

转载于:https://my.oschina.net/sefier/blog/39041

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值