依賴注入,控制反轉(zhuǎn)的好處?

原文地址: https://www.zhihu.com/question/23277575

要了解控制反轉(zhuǎn)( Inversion of Control ), 我覺得有必要先了解軟件設(shè)計(jì)的一個(gè)重要思想:依賴倒置原則(Dependency Inversion Principle )昔驱。

什么是依賴倒置原則?假設(shè)我們?cè)O(shè)計(jì)一輛汽車:先設(shè)計(jì)輪子上忍,然后根據(jù)輪子大小設(shè)計(jì)底盤骤肛,接著根據(jù)底盤設(shè)計(jì)車身,最后根據(jù)車身設(shè)計(jì)好整個(gè)汽車窍蓝。這里就出現(xiàn)了一個(gè)“依賴”關(guān)系:汽車依賴車身腋颠,車身依賴底盤,底盤依賴輪子吓笙。

這樣的設(shè)計(jì)看起來沒問題淑玫,但是可維護(hù)性卻很低。假設(shè)設(shè)計(jì)完工之后面睛,上司卻突然說根據(jù)市場(chǎng)需求的變動(dòng)混移,要我們把車子的輪子設(shè)計(jì)都改大一碼。這下我們就蛋疼了:因?yàn)槲覀兪歉鶕?jù)輪子的尺寸設(shè)計(jì)的底盤侮穿,輪子的尺寸一改,底盤的設(shè)計(jì)就得修改毁嗦;同樣因?yàn)槲覀兪歉鶕?jù)底盤設(shè)計(jì)的車身亲茅,那么車身也得改,同理汽車設(shè)計(jì)也得改——整個(gè)設(shè)計(jì)幾乎都得改狗准!

我們現(xiàn)在換一種思路克锣。我們先設(shè)計(jì)汽車的大概樣子,然后根據(jù)汽車的樣子來設(shè)計(jì)車身腔长,根據(jù)車身來設(shè)計(jì)底盤袭祟,最后根據(jù)底盤來設(shè)計(jì)輪子。這時(shí)候捞附,依賴關(guān)系就倒置過來了:輪子依賴底盤巾乳, 底盤依賴車身, 車身依賴汽車鸟召。

這時(shí)候胆绊,上司再說要改動(dòng)輪子的設(shè)計(jì),我們就只需要改動(dòng)輪子的設(shè)計(jì)欧募,而不需要?jiǎng)拥妆P压状,車身,汽車的設(shè)計(jì)了跟继。

這就是依賴倒置原則——把原本的高層建筑依賴底層建筑“倒置”過來种冬,變成底層建筑依賴高層建筑镣丑。高層建筑決定需要什么,底層去實(shí)現(xiàn)這樣的需求娱两,但是高層并不用管底層是怎么實(shí)現(xiàn)的莺匠。這樣就不會(huì)出現(xiàn)前面的“牽一發(fā)動(dòng)全身”的情況。

控制反轉(zhuǎn)(Inversion of Control)就是依賴倒置原則的一種代碼設(shè)計(jì)的思路谷婆。具體采用的方法就是所謂的依賴注入(Dependency Injection)慨蛙。其實(shí)這些概念初次接觸都會(huì)感到云里霧里的。說穿了纪挎,這幾種概念的關(guān)系大概如下:

為了理解這幾個(gè)概念期贫,我們還是用上面汽車的例子。只不過這次換成代碼异袄。我們先定義四個(gè)Class通砍,車,車身烤蜕,底盤封孙,輪胎。然后初始化這輛車讽营,最后跑這輛車虎忌。代碼結(jié)構(gòu)如下:

這樣,就相當(dāng)于上面第一個(gè)例子橱鹏,上層建筑依賴下層建筑——每一個(gè)類的構(gòu)造函數(shù)都直接調(diào)用了底層代碼的構(gòu)造函數(shù)膜蠢。假設(shè)我們需要改動(dòng)一下輪胎(Tire)類,把它的尺寸變成動(dòng)態(tài)的莉兰,而不是一直都是30挑围。我們需要這樣改:

由于我們修改了輪胎的定義,為了讓整個(gè)程序正常運(yùn)行糖荒,我們需要做以下改動(dòng):

由此我們可以看到杉辙,僅僅是為了修改輪胎的構(gòu)造函數(shù),這種設(shè)計(jì)卻需要修改整個(gè)上層所有類的構(gòu)造函數(shù)捶朵!在軟件工程中蜘矢,這樣的設(shè)計(jì)幾乎是不可維護(hù)的——在實(shí)際工程項(xiàng)目中,有的類可能會(huì)是幾千個(gè)類的底層泉孩,如果每次修改這個(gè)類硼端,我們都要修改所有以它作為依賴的類,那軟件的維護(hù)成本就太高了寓搬。

所以我們需要進(jìn)行控制反轉(zhuǎn)(IoC)珍昨,及上層控制下層,而不是下層控制著上層。我們用依賴注入(Dependency Injection)這種方式來實(shí)現(xiàn)控制反轉(zhuǎn)镣典。所謂依賴注入兔毙,就是把底層類作為參數(shù)傳入上層類,實(shí)現(xiàn)上層類對(duì)下層類的“控制”兄春。這里我們用構(gòu)造方法傳遞的依賴注入方式重新寫車類的定義:

這里我們?cè)侔演喬コ叽缱兂蓜?dòng)態(tài)的澎剥,同樣為了讓整個(gè)系統(tǒng)順利運(yùn)行,我們需要做如下修改:

看到?jīng)]赶舆?這里我只需要修改輪胎類就行了哑姚,不用修改其他任何上層類。這顯然是更容易維護(hù)的代碼芜茵。不僅如此叙量,在實(shí)際的工程中,這種設(shè)計(jì)模式還有利于不同組的協(xié)同合作和單元測(cè)試:比如開發(fā)這四個(gè)類的分別是四個(gè)不同的組九串,那么只要定義好了接口绞佩,四個(gè)不同的組可以同時(shí)進(jìn)行開發(fā)而不相互受限制;而對(duì)于單元測(cè)試猪钮,如果我們要寫Car類的單元測(cè)試品山,就只需要Mock一下Framework類傳入Car就行了,而不用把Framework, Bottom, Tire全部new一遍再來構(gòu)造Car。

這里我們是采用的構(gòu)造函數(shù)傳入的方式進(jìn)行的依賴注入。其實(shí)還有另外兩種方法:Setter傳遞接口傳遞。這里就不多講了,核心思路都是一樣的聘芜,都是為了實(shí)現(xiàn)控制反轉(zhuǎn)

看到這里你應(yīng)該能理解什么控制反轉(zhuǎn)和依賴注入了。那什么是控制反轉(zhuǎn)容器(IoC Container)呢蟆技?其實(shí)上面的例子中,對(duì)車類進(jìn)行初始化的那段代碼發(fā)生的地方沿侈,就是控制反轉(zhuǎn)容器闯第。

顯然你也應(yīng)該觀察到了,因?yàn)椴捎昧艘蕾囎⑷胱菏茫诔跏蓟倪^程中就不可避免的會(huì)寫大量的new咳短。這里IoC容器就解決了這個(gè)問題。這個(gè)容器可以自動(dòng)對(duì)你的代碼進(jìn)行初始化蛛淋,你只需要維護(hù)一個(gè)Configuration(可以是xml可以是一段代碼)咙好,而不用每次初始化一輛車都要親手去寫那一大段初始化的代碼。這是引入IoC Container的第一個(gè)好處褐荷。

IoC Container的第二個(gè)好處是:我們?cè)趧?chuàng)建實(shí)例的時(shí)候不需要了解其中的細(xì)節(jié)勾效。在上面的例子中,我們自己手動(dòng)創(chuàng)建一個(gè)車instance時(shí)候,是從底層往上層new的:

這個(gè)過程中层宫,我們需要了解整個(gè)Car/Framework/Bottom/Tire類構(gòu)造函數(shù)是怎么定義的杨伙,才能一步一步new/注入。

而IoC Container在進(jìn)行這個(gè)工作的時(shí)候是反過來的萌腿,它先從最上層開始往下找依賴關(guān)系限匣,到達(dá)最底層之后再往上一步一步new(有點(diǎn)像深度優(yōu)先遍歷):

這里IoC Container可以直接隱藏具體的創(chuàng)建實(shí)例的細(xì)節(jié),在我們來看它就像一個(gè)工廠:

我們就像是工廠的客戶毁菱。我們只需要向工廠請(qǐng)求一個(gè)Car實(shí)例米死,然后它就給我們按照Config創(chuàng)建了一個(gè)Car實(shí)例。我們完全不用管這個(gè)Car實(shí)例是怎么一步一步被創(chuàng)建出來贮庞。

實(shí)際項(xiàng)目中峦筒,有的Service Class可能是十年前寫的,有幾百個(gè)類作為它的底層贸伐。假設(shè)我們新寫的一個(gè)API需要實(shí)例化這個(gè)Service勘天,我們總不可能回頭去搞清楚這幾百個(gè)類的構(gòu)造函數(shù)吧?IoC Container的這個(gè)特性就很完美的解決了這類問題——因?yàn)檫@個(gè)架構(gòu)要求你在寫class的時(shí)候需要寫相應(yīng)的Config文件捉邢,所以你要初始化很久以前的Service類的時(shí)候脯丝,前人都已經(jīng)寫好了Config文件,你直接在需要用的地方注入這個(gè)Service就可以了伏伐。這大大增加了項(xiàng)目的可維護(hù)性且降低了開發(fā)難度宠进。

這里只是很粗略的講了一下我自己對(duì)IoC和DI的理解。主要的目的是在于最大限度避免晦澀難懂的專業(yè)詞匯藐翎,用盡量簡潔材蹬,通俗,直觀的例子來解釋這些概念吝镣。如果讓大家能有一個(gè)類似“哦堤器!原來就是這么個(gè)玩意嘛!”的印象末贾,我覺得就OK了闸溃。想要深入了解的話,可以上網(wǎng)查閱一些更權(quán)威的資料拱撵。這里推薦一下Dependency injection Inversion of Control Containers and the Dependency Injection pattern這兩篇文章辉川,講的很好很詳細(xì)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拴测,一起剝皮案震驚了整個(gè)濱河市乓旗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌集索,老刑警劉巖屿愚,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汇跨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡渺鹦,警方通過查閱死者的電腦和手機(jī)扰法,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毅厚,“玉大人塞颁,你說我怎么就攤上這事∥ⅲ” “怎么了祠锣?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咽安。 經(jīng)常有香客問我伴网,道長,這世上最難降的妖魔是什么妆棒? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任澡腾,我火速辦了婚禮,結(jié)果婚禮上糕珊,老公的妹妹穿的比我還像新娘动分。我一直安慰自己,他們只是感情好红选,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布澜公。 她就那樣靜靜地躺著,像睡著了一般喇肋。 火紅的嫁衣襯著肌膚如雪坟乾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天蝶防,我揣著相機(jī)與錄音甚侣,去河邊找鬼。 笑死间学,一個(gè)胖子當(dāng)著我的面吹牛渺绒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菱鸥,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躏鱼!你這毒婦竟也來了氮采?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤染苛,失蹤者是張志新(化名)和其女友劉穎鹊漠,沒想到半個(gè)月后主到,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躯概,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年登钥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娶靡。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡牧牢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出姿锭,到底是詐尸還是另有隱情塔鳍,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布呻此,位于F島的核電站轮纫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏焚鲜。R本人自食惡果不足惜掌唾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望忿磅。 院中可真熱鬧糯彬,春花似錦、人聲如沸贝乎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽览效。三九已至却舀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锤灿,已是汗流浹背挽拔。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留但校,地道東北人螃诅。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像状囱,于是被迫代替她去往敵國和親术裸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355