很長(zhǎng)一段時(shí)間里,我對(duì)控制反轉(zhuǎn)和依賴注入這兩個(gè)概念很模糊,閉上眼睛想一想,總有一種眩暈的感覺。但為了成為一名優(yōu)秀的 Java
工程師,我花了一周的時(shí)間,徹底把它們搞清楚了。
01、緊耦合
在我們編碼的過程中,通常都需要兩個(gè)或者更多的類通過彼此的合作來實(shí)現(xiàn)業(yè)務(wù)邏輯,也就是說,某個(gè)對(duì)象需要獲取與其合作對(duì)象的引用,如果這個(gè)獲取的過程需要自己實(shí)現(xiàn),代碼的耦合度就會(huì)高,維護(hù)起來的成本就比較高。
我們來通過實(shí)戰(zhàn)模擬一下。假如老王是少林寺的主持,他想讓小二和尚去掃達(dá)摩院的地,代碼可以這樣實(shí)現(xiàn)。
小二類的代碼如下所示:
public?class?Xiaoer?{
????public?void?saodi()?{
????????System.out.println("小二我在掃達(dá)摩院的地");
????}
}
老王類的代碼如下所示:
public?class?Laowang?{
????public?void?mingling()?{
????????new?Xiaoer().saodi();
????}
}
測(cè)試類的代碼如下所示:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????Laowang?laowang?=?new?Laowang();
????????laowang.mingling();
????}
}
Laowang 類的 mingling 方法中使用 new 關(guān)鍵字創(chuàng)建了一個(gè) Xiaoer
類的對(duì)象——這種代碼的耦合度就很高,維護(hù)起來的成本就很高,為什么這么說呢?
某一天,達(dá)摩院的地又臟了,老王主持想起了小二和尚,可小二和尚去練易筋經(jīng)了,讓誰去掃地呢,老王主持想起了小三和尚,于是 Laowang
類就不得不重新下一個(gè)新的命令,于是代碼變成了這樣:
public?class?Xiaosan?{
????public?void?saodi()?{
????????System.out.println("小三我在掃達(dá)摩院的地");
????}
}
public?class?Laowang?{
????public?void?mingling()?{
????????new?Xiaoer().saodi();
????}
????public?void?mingling1()?{
????????new?Xiaosan().saodi();
????}
}
假如小三和尚去挑水了,老王主持沒準(zhǔn)要下命令給小四和尚去掃達(dá)摩院的地。這樣下去的話,Laowang 這個(gè)類會(huì)瘋掉的。
老王主持覺得自己堂堂一屆高僧,下個(gè)掃地的命令竟然這樣麻煩,他覺得很不爽。
02、控制反轉(zhuǎn)
我們得替老王主持想個(gè)辦法對(duì)不對(duì)?
不如把這個(gè)掃地的差事交給老王的師弟老方吧,老方負(fù)責(zé)去叫小二和尚還是小三和尚還是小四和尚去執(zhí)行老王主持的命令。代碼可以這樣實(shí)現(xiàn)。
定義一個(gè)掃地和尚的接口,代碼如下所示:
public?interface?Heshang?{
????void?saodi();
}
小二類的代碼修改如下所示:
public?class?Xiaoer?implements?Heshang?{
????@Override
????public?void?saodi()?{
????????System.out.println("小二我在掃達(dá)摩院的地");????????
????}
????public?boolean?isYijinjing()?{
????????//?星期三的時(shí)候小二和尚要練易筋經(jīng)
????????return?false;
????}
}
小三類的代碼修改如下所示:
public?class?Xiaosan?implements?Heshang?{
????@Override
????public?void?saodi()?{
????????System.out.println("小三我在掃達(dá)摩院的地");????????
????}
}
老方類的代碼如下所示:
public?class?Laofang?{
????public?static?Heshang?getSaodiseng()?{
????????Xiaoer?xiaoer?=?new?Xiaoer();
????????if?(xiaoer.isYijinjing())?{
????????????return?new?Xiaosan();
????????}
????????return?xiaoer;
????}
}
如果老方確認(rèn)小二和尚在練易筋經(jīng),就叫小三和尚。
老王類的代碼修改如下所示:
public?class?Laowang?{
????public?void?mingling()?{
????????Laofang.getSaodiseng().saodi();
????}
}
測(cè)試類的代碼不改變,如下所示:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????Laowang?laowang?=?new?Laowang();
????????laowang.mingling();
????}
}
老王現(xiàn)在是不是省心多了,他只管下命令,該叫誰去掃達(dá)摩院的地由他師弟老方去負(fù)責(zé)。
我們替老王想的這個(gè)辦法就叫控制反轉(zhuǎn)(Inversion of Control,縮寫為 IoC),它不是一種技術(shù),而是一種思想——指導(dǎo)我們?cè)O(shè)計(jì)出松耦合的程序。
控制反轉(zhuǎn)從詞義上可以拆分為“控制”和“反轉(zhuǎn)”,說到控制,就必須找出主語和賓語,誰控制了誰;說到反轉(zhuǎn),就必須知道正轉(zhuǎn)是什么。
你看,在緊耦合的情況下,老王下命令的時(shí)候自己要通過 new
關(guān)鍵字創(chuàng)建依賴的對(duì)象(小二和尚或者小三和尚);而控制反轉(zhuǎn)后,老王要找的掃地和尚由他師弟老方負(fù)責(zé),也就是說控制權(quán)交給了老方,是不是反轉(zhuǎn)了呢?
03、依賴注入
依賴注入(Dependency Injection,簡(jiǎn)稱 DI)是實(shí)現(xiàn)控制反轉(zhuǎn)的主要方式:在類 A 的實(shí)例創(chuàng)建過程中就創(chuàng)建了依賴的 B
對(duì)象,通過類型或名稱來判斷將不同的對(duì)象注入到不同的屬性中。大概有 3 種具體的實(shí)現(xiàn)形式:
1)基于構(gòu)造函數(shù)。實(shí)現(xiàn)特定參數(shù)的構(gòu)造函數(shù),在新建對(duì)象時(shí)傳入所依賴類型的對(duì)象。
老王類的代碼修改如下所示:
public?class?Laowang?{
????private?Heshang?saodiseng;
????public?Laowang(Heshang?saodiseng)?{
????????this.saodiseng?=?saodiseng;
????}
????public?void?mingling()?{
???????this.saodiseng.saodi();
????}
}
測(cè)試類的代碼修改如下所示:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????Laowang?laowang?=?new?Laowang(new?Xiaosan());
????????laowang.mingling();
????}
}
這時(shí)候,控制權(quán)掌握在測(cè)試類的手里,它決定派小二和尚還是小三和尚去執(zhí)行老王的掃地命令。
2)基于 set 方法。實(shí)現(xiàn)特定屬性的 public set 方法,讓外部容器調(diào)用傳入所依賴類型的對(duì)象。
老王類的代碼修改如下所示:
public?class?Laowang?{
????private?Heshang?saodiseng;
????public?Heshang?getSaodiseng()?{
????????return?saodiseng;
????}
????public?void?setSaodiseng(Heshang?saodiseng)?{
????????this.saodiseng?=?saodiseng;
????}
????public?void?mingling()?{
???????this.getSaodiseng().saodi();
????}
}
測(cè)試類的代碼修改如下所示:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????Laowang?laowang?=?new?Laowang();
????????Xiaosan?xiaosan?=?new?Xiaosan();
????????laowang.setSaodiseng(xiaosan);
????????laowang.mingling();
????}
}
這時(shí)候,控制權(quán)仍然掌握在測(cè)試類的手里,它決定派小二和尚還是小三和尚去執(zhí)行老王的掃地命令。
3)基于接口。實(shí)現(xiàn)特定接口以供外部容器注入所依賴類型的對(duì)象,這種做法比較構(gòu)造函數(shù)和 set 方法更為復(fù)雜,這里就此略過。
可能有人會(huì)把控制反轉(zhuǎn)等同于依賴注入,但實(shí)際上它們有著本質(zhì)上的不同:控制反轉(zhuǎn)是一種思想,而依賴注入是實(shí)現(xiàn)控制反轉(zhuǎn)的一種形式。
04、Spring 框架
當(dāng)我們搞清楚控制反轉(zhuǎn)和依賴注入的概念后,就可以順帶了解一下大名鼎鼎的 Spring 框架??刂品崔D(zhuǎn)是 Spring 框架的核心,貫穿始終。Spring
中依賴注入有兩種實(shí)現(xiàn)方式:set 方式(傳值方式)和構(gòu)造器方式(引用方式)。
首先,我們需要在 pom.xml 文件中加入 Spring 的依賴項(xiàng),代碼如下所示:
<dependency>
????<groupId>org.springframework</groupId>
????<artifactId>spring-context-support</artifactId>
????<version>4.3.2.RELEASE</version>
</dependency>
其次,我們將 Laowang 類修改為如下內(nèi)容:
public?class?Laowang?{
????private?Heshang?saodiseng;
????public?Laowang(Heshang?saodiseng)?{
????????this.saodiseng?=?saodiseng;
????}
????public?void?mingling()?{
???????this.saodiseng.saodi();
????}
}
然后,我們創(chuàng)建一個(gè) Spring 的配置文件 application.xml,內(nèi)容如下所示:
<?xml?version="1.0"?encoding="UTF-8"?>
<beans?xmlns="http://www.springframework.org/schema/beans"
??xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
??xsi:schemaLocation=
"http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd">
??<bean?id="laowang"?class="com.cmower.java_demo.ioc.Laowang">
????<constructor-arg?ref="saodiseng"?/>
??</bean>
??<bean?id="saodiseng"?class="com.cmower.java_demo.ioc.Xiaosan"?/>
</beans>
通過 元素配置了兩個(gè)對(duì)象,一個(gè)老王主持,一個(gè)小三和尚,使用 元素將小三和尚作為老王主持的構(gòu)造參數(shù)。
準(zhǔn)備工作完成以后,我們來測(cè)試一下,代碼示例如下:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????ApplicationContext?context?=?new?ClassPathXmlApplicationContext(
"application.xml");
????????Laowang?laowang?=?(Laowang)?context.getBean("laowang");
????????laowang.mingling();
????}
}
你看,我們將控制權(quán)交給了 IoC 框架 Spring,這樣也可以完美的解決代碼耦合度較緊的問題。
05、最后
總結(jié)一下:
1)控制反轉(zhuǎn)是一種在軟件工程中解耦合的思想,把控制權(quán)交給了第三方,在運(yùn)行的時(shí)候由第三方?jīng)Q定將具體的依賴對(duì)象“注入”到調(diào)用類的對(duì)象中。
2)依賴注入可以作為控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式,將實(shí)例變量傳入到一個(gè)對(duì)象中去。
3)通過 IoC 框架,類 A 依賴類 B 的強(qiáng)耦合關(guān)系可以在運(yùn)行時(shí)通過容器建立,也就是說把創(chuàng)建 B 實(shí)例的工作移交給容器,類 A 只管使用就可以。
?
熱門工具 換一換