對依賴倒置原則(DIP)及Ioc、DI、Ioc容器的一些理解


1、概述

所謂依賴倒置原則(Dependence Inversion Principle)就是要依賴於抽象,不要依賴於具體。簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合,並由此引申出IoC、DI以及Ioc容器等概念。

 

2、意圖

面向過程的開發,上層調用下層,上層依賴於下層,當下層劇烈變動時上層也要跟着變動,這就會導致模塊的復用性降低而且大大提高了開發的成本。
 
面向對象的開發很好的解決了這個問題,一般情況下抽象的變化概率很小,讓用戶程序依賴於抽象,實現的細節也依賴於抽象。即使實現細節不斷變動,只要抽象不變,客戶程序就不需要變化。這大大降低了客戶程序與實現細節的耦合度。
 

3、正文

依賴倒置原則(DIP):一種軟件架構設計的原則(抽象概念)。

控制反轉(IoC):一種反轉流、依賴和接口的方式(DIP的具體實現方式)。

依賴注入(DI):IoC的一種實現方式,用來反轉依賴(IoC的具體實現方式)。

IoC容器:依賴注入的框架,用來映射依賴,管理對象創建和生存周期(DI框架)。

 

一個一個來說吧,首先先來了解下"依賴倒置原則(DIP)":

舉個生活中的小例子:

取過錢的朋友都知道,只要我們手上有一張銀行卡,我們就可以到各個銀行的ATM機上去取款,在這個場景中ATM機器屬於高層模塊,我們手上的銀行卡屬於底層模塊

在ATM機上提供了一個卡槽插口(接口),供各種銀行卡插入使用,在這里ATM機不依賴於具體的哪種銀行卡,它只規定了銀行卡的規格,只要我們手上的銀行卡滿足這個規格參數,我們就可以使用它。

轉換下概念,也就是:

高層模塊不依賴於底層模塊,而底層模塊依賴於高層模塊的接口(高層模塊定義接口,底層模塊負責實現)。

 

高層模塊(接口):抽象  底層模塊(實現接口):實現  ==>兩者應該依賴於抽象,抽象(高層)不依賴實現(底層),實現(底層)依賴於抽象(高層)。

 

再來舉個例子:

1、如果依賴不倒置將會出現:高層模塊依賴於底層模塊,也就是說底層變成了抽象,高層需要實現抽象出來的所有接口,一旦底層出現新的模塊,則就需要去修改高層的模塊,破壞了開放-封閉原則。

2、如果依賴倒置將會出現:底層模塊依賴於高層模塊,也就是說高層變成了抽象,底層只需要去實現高層的接口就行,一旦底層出現新的模塊,則高層模塊就不需要去修改(定義抽象接口不變)。

 

由此可見DIP的優點:

系統更柔韌:可以修改一部分代碼而不影響其他模塊。

系統更健壯:可以修改一部分代碼而不會讓系統崩潰。

系統更高效:組件松耦合,且可復用,提高開發效率。

 

 

接下來說下"控制反轉(Ioc)":

DIP是一種軟件設計原則,是告訴我們模塊之間應該是怎樣的一種關系,那Ioc就是具體的一種軟件設計模式,告訴我們應該如何去做,才能做到程序間的解耦。

Ioc(控制反轉)為高、低層模塊之間提供了抽象,也就是第三方系統,也就是依賴對象(底層對象)不在依賴的模塊中(高層模塊)中直接創建對象,而是把創建對象的權利交給第三次Ioc容器來創建,然后再把對象交給依賴模塊(聯想剛剛取錢的例子,ATM機器是高層模塊,它自身並沒有決定要插入哪個銀行的銀行卡,比如建行,農行,要插入什么卡的決定權在於我們(也就是第三方),我們插入什么行的卡,它就給我們什么銀行的服務)。

 

來個具體代碼感受下Ioc的好處:(訂單系統,底層操縱類是基於Mysql數據庫的)

MysqlHelper.java(數據庫操作類)

 1 package com.lcw.dip.test;
 2 
 3 public class MysqlHelper {
 4     
 5     public void add(){
 6         System.out.println("增加訂單..");
 7     }
 8 
 9     public void delete(){
10         System.out.println("刪除訂單..");
11     }
12     
13     public void update(){
14         System.out.println("修改訂單..");
15     }
16     
17     public void find(){
18         System.out.println("查詢訂單..");
19     }
20 }

 

Order.java(業務邏輯類)

 1 package com.lcw.dip.test;
 2 
 3 public class Order {
 4     private MysqlHelper helper = new MysqlHelper();
 5 
 6     public void addOrder() {
 7         this.helper.add();
 8     }
 9     
10     public void delOrder(){
11         this.helper.delete();
12     }
13     
14     public void updateOrder(){
15         this.helper.update();
16     }
17     
18     public void FindOrder(){
19         this.helper.find();
20     }
21 }

 

DipTest.java(測試類)

package com.lcw.dip.test;

/**
 *DIP(Dependence Inversion Principle)依賴倒置原則
 * @author Balla_兔子
 *
 */
public class DipTest {
    public static void main(String[] args) {
        Order order=new Order();
        order.addOrder();
    }

}

看下操作效果:

Perfect,完美!!

但如果現在突然業務需求要改換成Access數據庫,這時改怎么辦呢?

傳統的做法,我們需要再去編寫一個關於Access的數據庫操縱類,然后修改下Order類里的代碼,把實例化對象修改成Access類(new Access())。

那要是過幾天又要改成Oracle數據庫呢?

。。。。。反反復復,周而復始,煩!

 

有沒有什么辦法可以解決這個繁瑣的問題呢?答案是必須有!不然我就不用打這么多字了~~

接下來依賴注入(DI)就派上用場了:

依賴注入是實現Ioc的一種重要方式,將依賴的對象的創建權交給外部(第三方)來處理,而不是在自身new出一個實例。

例如上面的添加訂單例子,我們在創建數據庫操縱對象的時候是在Order類中直接new出,這樣有個很不好的地方就是,一旦數據庫變動,則我們還要去修改Order類,很顯然這是不可取的,違反了開放-封閉原則 。

那我們應該怎么做呢?答案很明顯就是利用DI(依賴注入),將創建對象的權利交給外部(第三方)實現,然后再傳遞給需要調用對象的模塊,也就是高層模塊。

傳遞注入的方式有三種:

1、構造注入:顧名思義利用構造方法注入

2、setter方法注入:在需要注入的類里提供一個setter方法

3、接口注入:因為具有代碼侵入性,一般很少用,前2種居多

 

說了這么多,上代碼直接看實例吧

DbHelper.java

 1 package com.lcw.dip.test;
 2 
 3 public class DbHelper {
 4     
 5     public void add(){
 6         System.out.println("增加訂單..");
 7     }
 8 
 9     public void delete(){
10         System.out.println("刪除訂單..");
11     }
12     
13     public void update(){
14         System.out.println("修改訂單..");
15     }
16     
17     public void find(){
18         System.out.println("查詢訂單..");
19     }
20 }

 

Order.java

 1 package com.lcw.dip.test;
 2 
 3 public class Order {
 4     //private MysqlHelper helper = new MysqlHelper();
 5     private DbHelper helper;
 6     public Order(DbHelper helper){//提供構造方法,注入屬性
 7         this.helper=helper;
 8     }
 9 
10     public void addOrder() {
11         this.helper.add();
12     }
13     
14     public void delOrder(){
15         this.helper.delete();
16     }
17     
18     public void updateOrder(){
19         this.helper.update();
20     }
21     
22     public void FindOrder(){
23         this.helper.find();
24     }
25 }

 

DipTest.java

 1 package com.lcw.dip.test;
 2 
 3 /**
 4  *DIP(Dependence Inversion Principle)依賴倒置原則
 5  * @author Balla_兔子
 6  *
 7  */
 8 public class DipTest {
 9     public static void main(String[] args) {
10         //Order order=new Order();
11         DbHelper helper=new DbHelper();
12         Order order=new Order(helper);//注入DbHelper對象
13         order.addOrder();
14     }
15 
16 }

 

效果依舊,這樣就很方便我們下次修改了,比如我們要換成Access數據庫,那么這次我們只需要去修改數據庫操縱類DbHelper就可以了,就不必要去動Order類了。

再來看下利用setter方法的注入:

 

DbHelper.java 數據庫操作底層類不變

 

Order.java

 1 package com.lcw.dip.test;
 2 
 3 public class Order {
 4 //    private MysqlHelper helper = new MysqlHelper();
 5 //    private DbHelper helper;
 6 //    public Order(DbHelper helper){//提供構造方法,注入屬性
 7 //        this.helper=helper;
 8 //    }
 9     
10     private DbHelper helper;
11     public void setHelper(DbHelper helper) {
12         this.helper = helper;
13     }
14 
15     public void addOrder() {
16         this.helper.add();
17     }
18     
19     public void delOrder(){
20         this.helper.delete();
21     }
22     
23     public void updateOrder(){
24         this.helper.update();
25     }
26     
27     public void FindOrder(){
28         this.helper.find();
29     }
30 }

 

DipTest.java

 1 package com.lcw.dip.test;
 2 
 3 /**
 4  *DIP(Dependence Inversion Principle)依賴倒置原則
 5  * @author Balla_兔子
 6  *
 7  */
 8 public class DipTest {
 9     public static void main(String[] args) {
10 //        Order order=new Order();
11 //        DbHelper helper=new DbHelper();
12 //        Order order=new Order(helper);//注入DbHelper對象
13         DbHelper helper=new DbHelper();
14         Order order=new Order();
15         order.setHelper(helper);
16         order.addOrder();
17     }
18 
19 }

效果依舊:

 

最后來說下關於Ioc容器:

在上面的例子中,我們都是通過手動的方式來創建依賴對象,然后在手動傳遞給被依賴模塊(高層),但對於大型的項目來說,各個組件之間的依賴關系式非常復雜的,如果我們還是用手動來創建依賴對象並且手動注入是個相當繁雜的一個工作,而且還容易出錯,甚至出現不可控狀態。

因此Ioc容器就這樣誕生了,也就是DI的一個框架,用來簡化我們的操作,Ioc容器可以做到動態創建、注入對象,對象的生命周期管理,映射依賴關系等。

Ioc容器有很多比如:PicoContainer,JBoss Microcontainer,Soto,Spring等。

 

總結一下:

DIP是軟件設計的一種思想,IoC則是基於DIP衍生出的一種軟件設計模式。

DI是IoC的具體實現方式之一,使用最為廣泛。

IoC容器是DI注入的框架,它管理着依賴項的生命周期以及映射關系。

 

 

作者:Balla_兔子
出處:http://www.cnblogs.com/lichenwei/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得准,我分文不收;相不准,你也好回來找我!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM