### 一、什么是依賴倒置原則
一種表述:
抽象不應當依賴于細節;細節應當依賴于抽象。
另一種表述:
要針對接口編程,不要針對實現編程。
針對接口編程的意思就是說,應當使用Java接口和抽象Java類進行變量的類型聲明、參量的類型聲明、方法的返回類型聲明,以及數據類型的轉換等。
不要針對實現編程的意思就是說,不應當使用具體Java類進行變量的類型聲明、參量的類型聲明、方法的返回類型聲明,以及數據類型的轉換等。
### 二、為什么要倒置
傳統的過程性系統的設計方法傾向于使高層次的模塊依賴于低層次的模塊,抽象層次依賴于具體層次。倒置原則就是要把這個錯誤的依賴關系倒轉過來。
抽象層次包含的是應用程序的商務邏輯和宏觀的、對整個系統來說重要的戰略性決定,是必然性的體現;而具體層次則含有一些次要的與實現有關的算法和邏輯,以及戰術性的決定,帶有相當大的偶然性選擇。具體層次的代碼經常會有變動,不能避免錯誤。抽象層次依賴于具體層次,使許多具體層次的細節的算法變化立即影響到抽象層次的宏觀的商務邏輯,**倒置微觀決定宏觀,戰術決定戰略,偶然決定必然,**這不是很荒唐嗎。
### 三、再看工廠方法模式
按照依賴倒置原則,客戶端應該依賴于對象的抽象類型而不是它的具體類型,但是在Java中使用new創建一個具體對象實例時必須調用具體類的構造方法,所以Java語言給出的類的實例無法做到只依賴于抽象類型。但是我們可以做到部分依賴,因為Java中有多態的特性。例如A是一個抽象類,B是繼承自A的一個具體子類,我們可以寫如下的代碼:
~~~
B b = new B();
~~~
這是一個完全依賴具體的不好的寫法,我們可以利用多態性寫成下面這樣:
~~~
A a = new B();
~~~
這樣就變成了部分依賴,前邊的A是依賴于抽象的,這樣當B換成其他的繼承自A的子類的時候,其他的程序是不會受到影響的。即使是這樣還是沒有做到完全依賴于抽象,在設計模式中前邊講過的工廠方法模式就解決了這個問題。

工廠方法模式將創建一個對象的工程封裝起來,客戶端僅僅得到這個實例化的結果,以及這個實例的抽象類型,當然工廠方法也不能避免要用new創建對象的限制,但是工廠模式將這個違反規則的做法推遲到了具體工廠角色中。將違反規則的做法孤立于易于控制的地方。
### 四、實例分析

這是一個銀行賬戶的例子,一個Account包含兩個部分,AccountType和AccountStatus,這兩個類都是抽象類,這兩個類分別有兩個具體的子類,Account在使用的是抽象類而不是子類,從而在Account的角度上是符合依賴倒置原則的。
~~~
package com.designphilsophy.dip;
/**
* 賬戶
* @author xingjiarong
*
*/
public class Account {
private AccountType accountType;
private AccountStatus accountStatus;
public Account(AccountType acctType) {
// write your code here
}
public void deposit(float amt) {
// write your code here
}
}
~~~
~~~
package com.designphilsophy.dip;
/**
* 賬戶的狀態是開的狀態還是超支狀態
* @author xingjiarong
*
*/
public abstract class AccountStatus
{
public abstract void sendCorrespondence();
}
~~~
~~~
package com.designphilsophy.dip;
/**
* 賬戶的類型是儲蓄卡還是支票
*
* @author xingjiarong
*
*/
public abstract class AccountType {
public abstract void deposit(float amt);
}
~~~
~~~
package com.designphilsophy.dip;
/**
* 支票
*
* @author xingjiarong
*
*/
public class Checking extends AccountType {
public void deposit(float amt) {
// write your code here
}
}
~~~
~~~
package com.designphilsophy.dip;
/**
* 儲蓄卡
*
* @author xingjiarong
*
*/
public class Savings extends AccountType {
public void deposit(float amt) {
// write your code here
}
}
~~~
~~~
package com.designphilsophy.dip;
/**
* 開狀態
*
* @author xingjiarong
*
*/
public class Open extends AccountStatus {
public void sendCorrespondence() {
// write your code here
}
}
~~~
在這個例子中,Account類依賴于AccountType和AccountStatus,但是這兩個類都是抽象類,如果我們想再添加一個新的狀態超支狀態的話,不會對其他的類造成影響。
~~~
package com.designphilsophy.dip;
/**
* 超支狀態
* @author xingjiarong
*
*/
public class Overdrawn extends AccountStatus {
public void sendCorrespondence() {
// write your code here
}
}
~~~
源碼下載:[http://download.csdn.net/detail/xingjiarong/9309387](http://download.csdn.net/detail/xingjiarong/9309387)
- 前言
- 設計原則(一)"開-閉"原則(OCP)
- 設計原則(二)里氏替換原則(LSP)
- 設計原則(三)組合復用原則
- 設計原則(四)依賴倒置原則(DIP)
- 設計模式(一)簡單工廠模式
- 設計模式(二)工廠方法模式
- 設計模式(三)抽象工廠模式
- 設計模式(四)單例模式
- 設計模式(五)創建者模式(Builder)
- 設計模式(六)原型模式
- 設計模式(七)門面模式(Facade Pattern 外觀模式)
- 設計模式(八)橋梁模式(Bridge)
- 設計模式(九)裝飾模式(Decorator)
- 設計模式(十)適配器模式
- 設計模式(十一)策略模式
- 設計模式(十二)責任鏈模式
- 設計模式之UML(一)類圖以及類間關系(泛化 、實現、依賴、關聯、聚合、組合)
- 設計模式之橋梁模式和策略模式的區別