好久写有关java技术的文章了,这几天在图书馆看到《Spring in Action》就借过来随手翻翻,觉得第一章的IoC的概念讲的很好,现在整理如下。
(1)从类的耦合说起
在实际的系统中,要多个类共同协作来完成某一项任务,一般称为复合。在系统的设计过程中,耦合是不可避免的,是必须的,但是它会带来以下问题:
--难以测试:看下面的两个类:
KnightOfTheRoundTable
|
| new
V
HolyGrailQuest
在KnightOfTheRoundTable类中使用了HolyGrailQuest类,并且可能使用不同的Quest类。这两个类的功能是骑士去执行不同的任务,HolyGrail只是代表了一种任务。这种通过国new的方式,使得knight和quest两个类紧密的耦合在一起。设计完成后做单元测试,看下面的代码:
KnightOfTheRoundTable knight = new KnightOfTheRoundTable("Bedivere");
HolyGrail grail = knight.embarkOnQuery();
assertNotNull(grail);
assertTrue(grail.isHoly());
在测试KnightOfTheRoundTable类的时候,间接的测试了HolyGrailQuest类,但是对于HolyGrailQuest类的情况并没有很显式的测试,所以使用了最后的两行代码来测试,显得很笨拙。不知道是否测试了所有可能的情况。
--难以维护
如果以后修改了代码,或者增加了/改变了knight的任务,代码必须改动,测试代码也要改动,并且改动一个地方,可能会影响到其他很多地方,为日后的维护工作带来了麻烦
--紧耦合
提示:这种模式引起的问题,主要是因为紧耦合所致,所以要解决这种问题的首要方案就是解耦合,但如何来解耦合呢?请继续看:
(2)解耦合——通过接口interface来实现
因为骑士可能执行不同的任务,也有不同种类的骑士,所以我们可以将任务和骑士抽象为接口,将具体的实现隐藏在接口之下,这样就不必在knight类中通过显示的 HolyGrailQuest quest = new HolyGrailQuest ()这样的语句来创建Quest对象,而可以通过接口Quest quest = new HolyGrailQuest()来实现。这样就形成了如下的类图:
这样,虽然使用了接口,使得层次分明,通过接口Quest来实现探险,但是还是只能从事一种探险任务,而如何才能让骑士从事任何一种任务呢?请继续看!
(3)给予与获得
骑士执行探险任务有两种方式:
第一、让骑士主动获得探险任务,前面的实现方式属于这类,通过new来实现;
第二,让骑士被动的获得任务,即给予骑士某项探险任务,这样就解决了上面的问题(让骑士可以从事任何一种任务,只要给他们分配即可)。
实际实现的方式很简单,看如下代码:
public void setQuest(Quest quest){
this.quest = quest;
}
简单的代码实现了方式的改变,这样给KnightOfTheRoundTable类传入任何的Quest任务,都可以接受了。
到这里可以很明显的看出来,我们将以前的new方式,翻转过来,即不是让主动获得依赖类,而是被动的获得依赖类,这就是IoC的核心,实际Fowler说得依赖注入也很贴切,即向类中“注入”它依赖的其他类。
好了,到这里,应该知道了IoC的基本概念了吧,也知道了IoC的基本功能了吧。
要想更深入的了解,请关注我的文章。