1.1、IoC是什么
Ioc—Inversion of Control,即“控制反轉(zhuǎn)”欣喧,不是什么技術(shù),而是一種設(shè)計(jì)思想梯找。在Java開發(fā)中唆阿,Ioc意味著將你設(shè)計(jì)好的對(duì)象交給容器控制,而不是傳統(tǒng)的在你的對(duì)象內(nèi)部直接控制锈锤。如何理解好Ioc呢驯鳖?理解好Ioc的關(guān)鍵是要明確“誰控制誰闲询,控制什么,為何是反轉(zhuǎn)(有反轉(zhuǎn)就應(yīng)該有正轉(zhuǎn)了)浅辙,哪些方面反轉(zhuǎn)了”扭弧,那我們來深入分析一下:
●誰控制誰,控制什么:傳統(tǒng)Java SE程序設(shè)計(jì)记舆,我們直接在對(duì)象內(nèi)部通過new進(jìn)行創(chuàng)建對(duì)象鸽捻,是程序主動(dòng)去創(chuàng)建依賴對(duì)象;而IoC是有專門一個(gè)容器來創(chuàng)建這些對(duì)象泽腮,即由Ioc容器來控制對(duì) 象的創(chuàng)建御蒲;誰控制誰?當(dāng)然是IoC 容器控制了對(duì)象诊赊;控制什么厚满?那就是主要控制了外部資源獲取(不只是對(duì)象包括比如文件等)碧磅。
●為何是反轉(zhuǎn)痰滋,哪些方面反轉(zhuǎn)了:有反轉(zhuǎn)就有正轉(zhuǎn),傳統(tǒng)應(yīng)用程序是由我們自己在對(duì)象中主動(dòng)控制去直接獲取依賴對(duì)象续崖,也就是正轉(zhuǎn);而反轉(zhuǎn)則是由容器來幫忙創(chuàng)建及注入依賴對(duì)象团搞;為何是反轉(zhuǎn)严望?因?yàn)橛扇萜鲙臀覀儾檎壹白⑷胍蕾噷?duì)象,對(duì)象只是被動(dòng)的接受依賴對(duì)象逻恐,所以是反轉(zhuǎn)像吻;哪些方面反轉(zhuǎn)了?依賴對(duì)象的獲取被反轉(zhuǎn)了复隆。
1.2拨匆、IoC能做什么
IoC 不是一種技術(shù),只是一種思想挽拂,一個(gè)重要的面向?qū)ο缶幊痰姆▌t惭每,它能指導(dǎo)我們?nèi)绾卧O(shè)計(jì)出松耦合、更優(yōu)良的程序亏栈。傳統(tǒng)應(yīng)用程序都是由我們?cè)陬悆?nèi)部主動(dòng)創(chuàng)建依賴對(duì)象台腥,從而導(dǎo)致類與類之間高耦合,難于測(cè)試绒北;有了IoC容器后黎侈,把創(chuàng)建和查找依賴對(duì)象的控制權(quán)交給了容器,由容器進(jìn)行注入組合對(duì)象闷游,所以對(duì)象與對(duì)象之間是 松散耦合峻汉,這樣也方便測(cè)試贴汪,利于功能復(fù)用,更重要的是使得程序的整個(gè)體系結(jié)構(gòu)變得非常靈活休吠。
其實(shí)IoC對(duì)編程帶來的最大改變不是從代碼上扳埂,而是從思想上,發(fā)生了“主從換位”的變化蛛碌。應(yīng)用程序原本是老大聂喇,要獲取什么資源都是主動(dòng)出擊,但是在IoC/DI思想中蔚携,應(yīng)用程序就變成被動(dòng)的了希太,被動(dòng)的等待IoC容器來創(chuàng)建并注入它所需要的資源了。
IoC很好的體現(xiàn)了面向?qū)ο笤O(shè)計(jì)法則之一—— 好萊塢法則:“別找我們酝蜒,我們找你”誊辉;即由IoC容器幫對(duì)象找相應(yīng)的依賴對(duì)象并注入,而不是由對(duì)象主動(dòng)去找亡脑。
1.3堕澄、IoC和DI
DI—Dependency Injection,即“依賴注入”:組件之間依賴關(guān)系由容器在運(yùn)行期決定霉咨,形象的說蛙紫,即由容器動(dòng)態(tài)的將某個(gè)依賴關(guān)系注入到組件之中。依賴注入的目的并非為軟件系統(tǒng)帶來更多功能途戒,而是為了提升組件重用的頻率坑傅,并為系統(tǒng)搭建一個(gè)靈活喷斋、可擴(kuò)展的平臺(tái)浆西。通過依賴注入機(jī)制顽腾,我們只需要通過簡(jiǎn)單的配置抄肖,而無需任何代碼就可指定目標(biāo)需要的資源憎瘸,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來自何處潮售,由誰實(shí)現(xiàn)酥诽。
理解DI的關(guān)鍵是:“誰依賴誰,為什么需要依賴咖驮,誰注入誰训枢,注入了什么”睦刃,那我們來深入分析一下:
●誰依賴于誰:當(dāng)然是應(yīng)用程序依賴于IoC容器十酣;
●為什么需要依賴:應(yīng)用程序需要IoC容器來提供對(duì)象需要的外部資源兴泥;
●誰注入誰:很明顯是IoC容器注入應(yīng)用程序某個(gè)對(duì)象虾宇,應(yīng)用程序依賴的對(duì)象文留;
●注入了什么:就是注入某個(gè)對(duì)象所需要的外部資源(包括對(duì)象燥翅、資源森书、常量數(shù)據(jù))凛膏。
IoC和DI由什么關(guān)系呢脏榆?其實(shí)它們是同一個(gè)概念的不同角度描述须喂,由于控制反轉(zhuǎn)概念比較含糊(可能只是理解為容器控制對(duì)象這一個(gè)層面,很難讓人想到誰來維護(hù)對(duì)象關(guān)系)掷伙,所以2004年大師級(jí)人物Martin Fowler又給出了一個(gè)新的名字:“依賴注入”,相對(duì)IoC 而言沛厨,“依賴注入”明確描述了“被注入對(duì)象依賴IoC容器配置依賴對(duì)象”绸栅。
看過很多對(duì)Spring的Ioc理解的文章粹胯,好多人對(duì)Ioc和DI的解釋都晦澀難懂风纠,反正就是一種說不清竹观,道不明的感覺臭增,讀完之后依然是一頭霧水誊抛,感覺就是開濤這位技術(shù)牛人寫得特別通俗易懂拗窃,他清楚地解釋了IoC(控制反轉(zhuǎn)) 和DI(依賴注入)中的每一個(gè)字随夸,讀完之后給人一種豁然開朗的感覺。我相信對(duì)于初學(xué)Spring框架的人對(duì)Ioc的理解應(yīng)該是有很大幫助的伍俘。
用php例子來解釋控制反轉(zhuǎn)和依賴注入
先看一個(gè)例子:
<?php
class A
{
public $b;
public $c;
public function A()
{
//TODO
}
public function Method()
{
$this->b=new B();
$this->c=new C();
$this->b->Method();
$this->c->Method();
//TODO
}
}
class B
{
public function B()
{
//TODO
}
public function Method()
{
//TODO
echo 'b';
}
}
class C
{
public function C()
{
//TODO
}
public function Method()
{
//TODO
echo 'c';
}
}
$a=new A();
$a->Method();
?>
上面代碼,我們很容易理解一句話:
A類依賴B類和C類
也就是說妇萄,如果今后開發(fā)過程中咬荷,要對(duì)B類或者C類修改冠句,一旦涉及函數(shù)改名,函數(shù)參數(shù)數(shù)量變動(dòng)幸乒,甚至整個(gè)類結(jié)構(gòu)的調(diào)整懦底,我們也要對(duì)A類做出相應(yīng)的調(diào)整,A類的獨(dú)立性喪失了罕扎,這在開發(fā)過程中是很不方便的聚唐,也就是我們說的“牽一發(fā)動(dòng)全身”,如果兩個(gè)類是兩個(gè)人分別寫的腔召,矛盾往往就在這個(gè)時(shí)候產(chǎn)生了杆查。。臀蛛。
萬一真的要改動(dòng)B類和C類亲桦,有沒有辦法,可以不去改動(dòng)或者盡量少改動(dòng)A類的代碼呢浊仆?這里要用到控制反轉(zhuǎn)客峭。
高層模塊不應(yīng)該依賴于底層模塊,兩個(gè)都應(yīng)該依賴抽象抡柿。
控制反轉(zhuǎn)(IOC)是一種思想舔琅,依賴注入(DI)是實(shí)施這種思想的方法。
第一種方法叫做:構(gòu)造器注入(這種方法也不推薦用沙绝,但比不用要好)
class A
{
public $b;
public $c;
public function A($b,$c)
{
$this->b=$b;
$this->c=$c;
}
public function Method()
{
$this->b->Method();
$this->c->Method();
}
}
客戶端類這樣寫:
$a=new A(new B(),new C());
$a->Method();
A類的構(gòu)造器依賴B類和C類,通過構(gòu)造器的參數(shù)傳入鼠锈,至少實(shí)現(xiàn)了一點(diǎn)闪檬,就是B類對(duì)象b和C類對(duì)象c的創(chuàng)建都移至了A類外,所以一旦B類和C類發(fā)生改動(dòng)购笆,A類無需做修改粗悯,只要在client類里改就可以了
假如有一天,我們需要擴(kuò)充B類同欠,做兩個(gè)B類的子類:
class B
{
public function B()
{
//TODO
}
public function Method()
{
//TODO
echo 'b';
}
}
class B1 extends B
{
public function B1()
{
//TODO
}
public function Method()
{
echo 'b1';
}
}
class B2 extends B
{
public function B2()
{
//TODO
}
public function Method()
{
echo 'b2';
}
}
也很簡(jiǎn)單样傍,客戶端類這么寫:
$a=new A(new B2(),new C());
$a->Method();
所以A類是不用關(guān)心B類到底有哪些個(gè)子類的横缔,只要在客戶端類關(guān)心就可以了。
第二種方法叫做:工廠模式注入(推薦使用)
class Factory
{
public function Factory()
{
//TODO
}
public function create($s)
{
switch($s)
{
case 'B':
{
return new B();
break;
}
case 'C':
{
return new C();
break;
}
default:
{
return null;
break;
}
}
}
}
我們A類代碼改為:
class A
{
public $b;
public $c;
public function A()
{
//TODO
}
public function Method()
{
$f=new Factory();
$this->b=$f->create('B');
$this->c=$f->create('C');
$this->b->Method();
$this->c->Method();
//TODO
}
}
其實(shí)已經(jīng)解耦了一小部分衫哥,至少如果B類和C類的構(gòu)造函數(shù)要是發(fā)生變化茎刚,比如修改函數(shù)參數(shù)等,我們只需要改Factory類就可以了撤逢。
把B類和C類中的方法再抽象出來膛锭,做一個(gè)接口
interface IMethod
{
public function Method();
}
這樣,A類中的b變量和c變量就不再是一個(gè)具體的變量了蚊荣,而是一個(gè)抽象類型的變量初狰,不到運(yùn)行那一刻,不知道他們的Method方式是怎么實(shí)現(xiàn)的互例。
class B implements IMethod
{
public function B()
{
//TODO
}
public function Method()
{
//TODO
echo 'b';
}
}
class C implements IMethod
{
public function C()
{
//TODO
}
public function Method()
{
//TODO
echo 'c';
}
}
總結(jié)幾點(diǎn):
1.我們把A類中的B類對(duì)象和C類對(duì)象的創(chuàng)建移至A類外
2.原本A類依賴B類和C類奢入,現(xiàn)在變成了A依賴Factory,F(xiàn)actory依賴B和C媳叨。