四 : DIP(依賴倒置原則)

hkmexu發表於2009-03-11

Dependency Inversion Principle , DIP

******************************************************************************************

表述:

1.         Abstractions should not depend upon details, details should depend upon abstractions.

2.         Program to an interface, not an implementation.

3.         面向介面程式設計。

4.         抽象不應依賴具體,具體應依賴抽象。

5.         針對介面程式設計,而不是針對實現程式設計。

 

   傳統的依賴VS.倒置的依賴。

     向介面程式設計的意思是說,應當使用介面和抽象類進行變數的型別宣告,引數的型別宣告,方法的返還型別宣告,以及資料型別的轉換等。

  不要針對實現程式設計的意思就是說,不應當使用具體類進行變數的型別宣告,引數型別宣告,方法的返還型別宣告,以及資料型別的轉換等。 要保證做到這一點,一個具體的類應只實現介面和抽象類中宣告過的方法,而不應釋出(public)另外的方法。 只要一個被引用的物件存在抽象型別,就應當在任何引用此物件的地方使用抽象型別,包括引數的型別宣告,方法返還型別的宣告,屬性變數的型別宣告等。

 

     一個抽象類的實現只能由這個抽象類的子類給出,也就是說,這個實現處在抽象類所定義出的繼承的等級結構中。而有些語言(如Java)都限制一個類只能從最多一個超類繼承,因此將抽象類作為型別定義工具的效能大打折扣。反過來,看介面,就會發現:任何一個實現了一個介面所規定的方法的類都可以具有這個介面的型別,而一個類可以實現任意多個介面。 然而,如果一個類需要實現兩個介面,而這兩個介面中存在相同signature的函式,則又當如何?  :)

   介面VS.抽象類:

   介面與抽象類的區別就在於抽象類可以提供某些方法的部分實現,而介面則不可以。這也大概是抽象類唯一的優點。如果向一個抽象類加入一個新的具體方法,那麼所有的子類一下子就都得到了這個新的具體方法,而介面做不到這一點。如果向一個介面加入了一個新的方法,則所有實現這個介面的類就全部不能通過編譯了,因為它們都沒有實現這個新宣告的方法。這顯然是介面的一個缺點。

 

 

       從程式碼重構的角度上講,將一個單獨的具體類重構成一個介面是很容易的。只需要宣告一個介面,並將相應的方法新增到介面宣告中,然後在具體類定義語句中加上保留字以繼承於該介面就行了。 而作為一個已有的具體類新增一個抽象類作為抽象型別則沒有那麼容易,因為這個具體類有可能已經有一個超類。這樣一來,這個新定義的抽象類只好繼續向上移動,變成這個超類的超類。如此迴圈,最後這個新的抽象類必定處於整個型別等級結構的最上端,從而使等級結構中的所有成員都會受到影響。這樣就偏離了原來設計的意圖。

      介面是定義混合型別的理想工具。所為混合型別,就是在一個類的主型別之外的次要型別。一個混合型別表明一個類不僅僅具有某個主型別的行為,而且具有其他的次要行為。

 

     聯合使用介面和抽象類:

   由於抽象類具有提供預設實現的優點,而介面具有其他所有優點,所以聯合使用兩者就是一個很好的選擇。 首先,宣告型別的工作仍然由介面承擔,但是同時給出的還有一個抽象類,以給出一個預設實現。其他同屬於這個抽象型別的具體類可以選擇實現這個介面,也可以選擇繼承自這個抽象類。如果一個具體類直接實現這個介面的話,它就必須自行實現所有的介面;相反,如果它繼承自抽象類的話,它可以省去實現一些方法,因為它可以從抽象類中自動得到這些方法的預設實現;如果需要向介面加入一個新的方法,那麼只要向這個抽象類加入這個方法的一個具體實現就可以了,因為所有繼承自這個抽象類的子類都會從這個抽象類得到這個具體方法。這其實就是預設介面卡模式(Default Adapter).

 

  

   依賴倒置原則DIP告訴我們:應該優先依賴於抽象類,而避免依賴於具體類。其中的原因就在於:這些具體類有可能會改變。但是,如果這個具體類是穩定的,那麼依賴它就不會出現麻煩。

   

 

 

 

 

 

相關文章