JAVA IOC 與 DI

weixin_33670713發表於2016-01-06

依賴倒置、控制反轉和依賴注入的區分

依賴倒置、控制反轉和依賴注入的區分依賴倒置(Dependency Inversion Principle)、控制反轉(Inversion of Control)和依賴注入(Dependency Injection)從思想來講是統一的,或者說是類似的,有人也說它們是同一個東西。但是還是可以做一點區分:

依賴倒置原則      是進行軟體設計時考慮遵循的一個原則。具體為:
      (1)上層模組不應該依賴於下層模組,它們共同依賴於一個抽象。
      (2)抽象不能依賴於具象,具象依賴於抽象。
控制反轉      是軟體執行時體現出來的一個特徵:如果物件A執行時依賴於物件B,但A並不去建立B,而是從外界直接取得B。也就是說,一個物件並不是自己去建立它所依賴的其它物件。
依賴注入      是控制反轉的一種實現手段。如上面的例子,B的取得並不需要A的干涉,而是利用某些框架在通過構造引數或屬性設定來實現。

IOC:Inversion of Control 控制反轉
DI:Dependency Injection 依賴注入
控制反轉,從字面意思來看,就是控制權又被動變主動,最後又變回被動。
舉個例子:
你的主管要求你做一件事情,這個時候就存在這麼幾個過程:

  1. 主管命令你做事情(這個時候主動權在主管,你是被動的)
  2. 你接到命令做事情(這個時候主題是你,你是主動的,控制權在你手裡)
  3. 你完成事情(這個時候主題依然是你,控制權在你手裡)
  4. 報告主管做完事情(主動權又叫交到主管手裡了)

上面的整個過程就完成了一次IOC,從上面可以看出,IOC的基本思想是控制權的轉換過程。
舉個程式碼的例子:
假如有Class A,Class B,在A內部會初始化一個B,呼叫B的一個方法DoMethod

public Class B{
public void DoMethod()
{
//do somthing;
}
}
public Class A
{
public void Excute()
{
B b=newB();
b.DoMethod();
}
}

假如在Main函式中如下執行:
A a=newA();
a.Excute();
從這兩行程式碼來看,事實上也存在一個IOC的過程,a——>b——>a,理解的關鍵點就在在A的內部呼叫Excute的時候,方法b.DoMethod的執行。
理解了IOC,我們再看一下DI。
從上面A呼叫B我們可以看出,在初始化一個A的例項時,也必須例項化一個B,也就是說如果沒有B或者B出了問題,A就無法例項化,這就產生了一種依賴,就是A依賴B,這種依賴從設計的角度來說就是耦合,顯然它是無法滿足高內聚低耦合的要求的。這個時候就需要解耦,當然解耦有很多種方法,而DI就是其中一種。不管任何一種解耦方法,都不是說使A和B完全沒有關係,而是把這種關係的實現變得隱晦,不那麼直接,但是又很容易實現,而且易於擴充套件,不像上面的程式碼那樣,直接new一個B出來。
那為什麼我們總是把IOC和DI聯絡到一起呢?是因為DI的基本思想就是IOC,而體現IOC 思想的方法還有另外一個,那就是Service Locator,這個方法好像涉及到的很少。
DI,依賴注入,從字面意思就可以看出,依賴是通過外接注入的方式來實現的。這就實現瞭解耦,而DI的方式通常有三種,

  1. 構造器注入
  2. 屬性設定器注入
  3. 介面注入(我感覺介面注入是同時存在於上兩種注入方式的,而不應該獨立出來)
    以上的闡述只是為了先讓我們能對IOC和DI有一個感性的理解,那麼IOC真正解決的問題是什麼呢?
    我們講了那麼多主動被動的問題,那我們是從什麼視角來看待這個問題的呢?
    所謂為什麼你是主動,而我不是主動呢?這就需要一個參照物,那這個參照物是什麼呢?就是容器,在容器中來體現主動和被動。
    用白話來講,就是由容器控制程式之間的關係,而非傳統實現中,由程式程式碼直接操控。這也就是所謂“控制反轉”的概念所在:控制權由應用程式碼中轉到了外部容器,控制權的轉移,是所謂“反轉”,這是通常對IOC的一個解釋。
    從容器的角度來看主動和被動,和由容器來控制程式之間的關係,應該是相通的,是一個意思。
    IOC要解決的就是程式之間呼叫的一個問題,它應該是一個思想層面的東西,是一箇中心,就像一支樂隊的指揮,而程式就是樂器,通過指揮來協調各種樂器,來演奏出美好的音樂來。
    以下文字參考:http://www.cnblogs.com/gooddasenlin/archive/2009/03/02/1401631.html
    Interface Driven Design 介面驅動
    介面驅動有很多好處,可以提供不同靈活的子類實現,增加程式碼穩定和健壯性等等,但是介面一定是需要實現的,也就是如下語句遲早要執行:AInterface a = new AInterfaceImp(); 這樣一來,耦合關係就產生了。
    如:
Class A
{
AInterface a;
A(){}
aMethod()
{
a=newAInterfaceImp();
}
}

ClassA與AInterfaceImp就是依賴關係,如果想使用AInterface的另外一個實現就需要更改程式碼了。
當然我們可以建立一個Factory來根據條件生成想要的AInterface的具體實現,即:

InterfaceImplFactory
{
AInterface create(Object condition)
{
if(condition=condA)
{
returnnewAInterfaceImpA();
}
elseif(condition=condB)
{
returnnewAInterfaceImpB();
}
else
{
returnnewAInterfaceImp();
}
}
}

表面上是在一定程度上緩解了以上問題,但實質上這種程式碼耦合並沒有改變。
通過IoC模式可以徹底解決這種耦合,它把耦合從程式碼中移出去,放到統一的XML檔案中,通過一個容器在需要的時候把這個依賴關係形成,即把需要的介面實現注入到需要它的類中,這可能就是“依賴注入”說法的來源了。
IOC模式系統中,通過引入實現IOC模式的IOC容器,即可由IOC容器來管理物件的生命週期、依賴關係等,從而使得應用程式的配置和依賴性規範與實際的應用程式程式碼分開。其中一個特點就是通過文字的配置檔案進行應用程式元件間相互關係的配置,而不用重新修改並編譯具體的程式碼。
當前比較知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等。其中,輕量級的有Pico Container、Avalon、Spring、HiveMind等,超重量級的有EJB,而半輕半重的有容器有JBoss,Jdon等。
可以把IoC模式看做是工廠模式的昇華,可以把IoC看作是一個大工廠,只不過這個大工廠裡要生成的物件都是在XML檔案中給出定義的,然後利用Java 的“反射”程式設計,根據XML中給出的類名生成相應的物件。從實現來看,IoC是把以前在工廠方法裡寫死的物件生成程式碼,改變為由XML檔案來定義,也就是把工廠和物件生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性。
IoC中最基本的Java技術就是“反射”程式設計。反射又是一個生澀的名詞,通俗的說反射就是根據給出的類名(字串)來生成物件。這種程式設計方式可以讓物件在生成時才決定要生成哪一種物件。反射的應用是很廣泛的,象Hibernate、String中都是用“反射”做為最基本的技術手段。
IoC最大的好處是什麼?因為把物件生成放在了XML裡定義,所以當我們需要換一個實現子類將會變成很簡單(一般這樣的物件都是現實於某種介面的),只要修改XML就可以了。
參考:http://www.cnblogs.com/niuniu1985/archive/2010/01/13/1646375.html

相關文章