1. 程式人生 > >OO設計原則 -- Dependency Inversion Principle:依賴倒置原則(DIP)

OO設計原則 -- Dependency Inversion Principle:依賴倒置原則(DIP)

依賴倒置原則的2個重要方針

A. High level modules should not depend upon low level modules. Both shoulddepend upon abstractions.

高層模組不應該依賴於低層模組,二者都應該依賴於抽象
B. Abstractions should not depend upon details. Details should depend uponabstractions.
抽象不應該依賴於細節,細節應該依賴於抽象

概念解說:

依賴:在程式設計中,如果一個模組a使用/呼叫了另一個模組b,我們稱模組a依賴模組b。

低層模組:往往在一個應用程式中,我們有一些低層次的類,這些類實現了一些

基本的或初級的操作,我們稱之為低層模組;

高層模組:另外有一些高層次的類,這些類封裝了某些複雜的邏輯,並且依賴於

低層次的類,這些類我們稱之為高層模組。

我們現在來看看依賴有幾種,依賴也就是耦合,分為下面三種:

----- 零耦合(Nil Coupling)關係,兩個類沒有依賴關係,那就是零耦合。

----- 具體耦合(Concrete Coupling)關係,兩個具體的類之間有依賴關係,

那麼就是具體耦合關係,如果一個具體類直接引用另外一個具體類就會發

生這種關係。

-----抽象耦合(Abstract Coupling)關係,這種關係發生在一個具體類和一個抽

象類之間,這樣就使必須發生關係的類之間保持最大的靈活性。


為什麼叫做依賴倒置(DependencyInversion)呢?
面向物件程式設計相對於面向過程(結構化)程式設計而言,依賴關係被倒置了。因為傳統的結構化程式設計中,高層模組總是依賴於低層模組。

依賴倒置(Dependence InversionPrinciple)原則講的是:要依賴於抽象,不要依賴於具體。

簡單的說,依賴倒置原則要求客戶端依賴於抽象耦合。原則表述:

抽象不應當依賴於細節;細節應當依賴於抽象;

要針對介面程式設計,不針對實現程式設計。

問題的提出

Robert C. Martin氏在原文中給出了“Bad Design”的定義:
1. It is hard to change because every change affects too many other parts ofthe system.(Rigidity)
系統很難改變,因為每個改變都會影響其他很多部分。
2. When you make a change, unexpected parts of the system break. (Fragility)
當你對某地方做一修改,系統的看似無關的其他部分都不工作了。
3. It is hard to reuse in another application because it cannot be disentangledfrom the current application. (Immobility)
系統很難被另外一個應用重用,因為你很難將要重用的部分從系統中分離開來。

導致“Bad Design”的很大原因是“高層模組”過分依賴“低層模組”。
一個良好的設計應該是系統的每一部分都是可替換的。
如果“高層模組”過分依賴“低層模組”:

一方面一旦“低層模組”需要替換或者修改,“高層模組”將受到影響;

另一方面,高層模組很難可以重用。

比如,一個Copy模組,需要把來自Keyboard的輸入複製到Print,

即使對Keyboard和Print的封裝已經做得非常好,但如果Copy模組裡直接使用Keyboard與Print,

Copy任很難被其他應用環境(比如需要輸出到磁碟時)重用。

問題的解決:

為了解決上述問題,Robert C. Martin氏提出了OO設計的Dependency Inversion Principle (DIP) 原則。

DIP給出了一個解決方案:

在高層模組與低層模組之間,引入一個抽象介面層。

High Level Classes(高層模組)  -->

Abstraction Layer(抽象介面層) -->

Low Level Classes(低層模組)

抽象介面是對低層模組的抽象,低層模組繼承或實現該抽象介面。
這樣,高層模組不直接依賴低層模組,高層模組與低層模組都依賴抽象介面層。
當然,抽象也不依賴低層模組的實現細節,低層模組依賴(繼承或實現)抽象定義。

Robert C. Martin氏給出的DIP方案的類的結構圖:


PolicyLayer -->

MechanismInterface(abstract) -->

MechanismLayer -->

UtilityInterface(abstract) -->

UtilityLayer


類與類之間都通過Abstract Layer來組合關係。

 例項說明DIP

反面例子:


缺點:耦合太緊密,Light發生變化將影響ToggleSwitch。

解決辦法一:
將Light作成Abstract,然後具體類繼承自Light。


優點:ToggleSwitch依賴於抽象類Light,具有更高的穩定性,而BulbLight與TubeLight繼承自Light,可以根 據"開放-封閉"原則進行擴充套件。只要Light不發生變化,BulbLight與TubeLight的變化就不會波及ToggleSwitch。

缺點:如果用ToggleSwitch控制一臺電視就很困難了。總不能讓TV繼承自Light吧。

解決方法二:



優點:更為通用、更為穩定。

總結

 DIP要求客戶端依賴於抽象耦合,抽象不應當依賴於細節,細節應當依賴於抽象(Abstractionsshould not depend upon details. Details should depend upon abstractions),這 個原則的另外一個表述就是"四人團"強調的那個:要針對介面程式設計,不要對實現程式設計(Program to aninterface, not an implementation),程式在需要引用一個物件時,應當儘可能的使用抽象型別作為變數的靜態型別,這就是針對介面程式設計的含義。DIP是達到"開-閉"原則的途徑。

要做到DIP,用抽象方式耦合是關鍵。由於一個抽象耦合總要涉及具體類從抽象類繼承。並且需要保證在任何引用到某類的地方都可以改換成其子類,因此,LSP是DIP的基礎。DIP是OOD的核心原則,設計模式的研究和應用都是用它作為指導原則的。DIP雖然強大,但是也很難實現。另外DIP是假定 所有的具體類都會變化,這也不是全對,有些具體類就相當穩定。使用這個類的客戶端就完全可以依賴這個具體類而不用再弄一個抽象類。