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

一、概述

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

 

二、意圖

面向過程的開發,上層調用下層,上層依賴於下層,當下層劇烈變更時上層也要跟着變更,這就會致使模塊的複用性下降並且大大提升了開發的成本。
 
面向對象的開發很好的解決了這個問題,通常狀況下抽象的變化機率很小,讓用戶程序依賴於抽象,實現的細節也依賴於抽象。即便實現細節不斷變更,只要抽象不變,客戶程序就不須要變化。這大大下降了客戶程序與實現細節的耦合度。
 

三、正文

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

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

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

IoC容器:依賴注入的框架,用來映射依賴,管理對象建立和生存週期(DI框架)。設計模式

 

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

舉個生活中的小例子:框架

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

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

轉換下概念,也就是:spa

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

 

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

 

再來舉個例子:

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

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

 

因而可知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(依賴注入),將建立對象的權利交給外部(第三方)實現,而後再傳遞給須要調用對象的模塊,也就是高層模塊。

傳遞注入的方式有三種:

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

二、setter方法注入:在須要注入的類裏提供一個setter方法

三、接口注入:由於具備代碼侵入性,通常不多用,前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/本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,往後必有一番做爲!旁邊有「推薦」二字,你就順手把它點了吧,相得準,我分文不收;相不許,你也好回來找我!

相關文章
相關標籤/搜索