依賴倒置原則DIP(Dependence Inversion Principle) 依賴倒置原則的含義 高層模塊不能依賴低層模塊,二者都應該依賴其抽象。 抽象不應該依賴於細節。 細節應該依賴抽象。 什麼是 高層模塊?低層模塊 ? 每一個原子邏輯就是低層模塊,原子邏輯再組就是高層模塊。 什麼是 抽象和 ...
依賴倒置原則DIP(Dependence Inversion Principle)
依賴倒置原則的含義
- 高層模塊不能依賴低層模塊,二者都應該依賴其抽象。
- 抽象不應該依賴於細節。
- 細節應該依賴抽象。
什麼是高層模塊?低層模塊?
每一個原子邏輯就是低層模塊,原子邏輯再組就是高層模塊。
什麼是抽象和細節?
抽象是抽象類,不可被實例化。
細節是實現類,比如實現的介面或繼承抽象類的子類,可以被實例化。
表現在Java語言中就是面向介面編程
- 模塊間的依賴是通過抽象來實現的,具體的實現類之間不能發生直接的依賴。
- 介面或抽象類不能依賴與實現類。
- 實現類依賴介面或抽象類。
***
我們假設有三個類,一個為場景類,一個為司機類,一個為賓士類。通過這三個類我們便可以實現司機開動汽車這個場景。如圖
具體的實現代碼如下
司機類
package des.DIP;
//司機類
public class Driver {
//司機駕駛車 緊耦合
public void drive(Benze benze){
benze.run();
}
//司機駕駛寶馬車 緊耦合
public void drive(BMW bmw){
bmw.run();
}
}
賓士類
package des.DIP;
//賓士車
public class Benze {
public void run(){
System.out.print("賓士車開始運行...");
}
}
場景類
package des.DIP;
//場景類
public class Client {
public static void main(String[] args){
//創建一個司機
Driver zs = new Driver();
//創建一個賓士車
Benze benze = new Benze();
//司機可以開賓士車
zs.drive(benze);
//假設此時增加一個寶馬車呢?還要再增加一個方法,並且重新創建
//一個還好若是很多呢?難道要在司機類聲明很多方法嗎?
BMW bmw = new BMW();
}
}
package des.DIP;
//寶馬車
public class BMW {
//寶馬車當然也可以開動
public void run(){
System.out.print("寶馬車開動...");
}
}
程式正常的寫法就是如此,但是如果我們考慮下麵一個問題,司機並不是只會開著一輛Benze牌的車,假如我們再假如一個BMW(寶馬)牌的車,我們傳統的做法就是再新建一個類,然後再司機類中再添加一個drive BMW的方法。假如我們要添加無數品牌的汽車呢,難道還要再司機類中添加無數的drive方法嗎?他們都有著相同的方法名,只是傳入的汽車型號不同。
顯然,傳統的drive方法的寫法,具有緊耦合性,只要車型變更,就不能再使用了。其導致的結果就是系統的可維護性大大降低,可讀性也大大降低。
*
解決方法
使用依賴倒置原則**
DIP第一種方法 介面註入法
建立兩個介面,IDriver和ICar
此時業務的場景類就可以改寫成如下
package des.DIP;
public class Client1 {
public static void main(String[] args){
//創建一個司機
/**
* 此處明確兩個概念:
* IDriver 叫做錶面類型, Driver1 叫做實際類型 或稱抽象類型和實際類型
*
* 此後所有的操作均是對抽象介面的操作,具體屏蔽了細節
*/
IDriver ds = new Driver1();
ICar c = new Bmw1();
ds.drive(c);
}
}
錶面類型和實際類型: IDriver 叫做錶面類型, Driver1 叫做實際類型 或稱抽象類型和實際類型
下麵是介面類和實現類參考代碼:
package des.DIP;
//司機介面
public interface IDriver {
//司機可以駕駛汽車,什麼汽車不用管即抽象類(松耦合)
public void drive(ICar car);
}
package des.DIP;
//抽象汽車類
public interface ICar {
//汽車啟動
public void run();
}
package des.DIP;
public class Driver1 implements IDriver {
@Override
public void drive(ICar car) {
car.run();
}
}
package des.DIP;
public class Bmw1 implements ICar {
@Override
public void run() {
System.out.print("寶馬車開始運行...");
}
}
package des.DIP;
public class Benze1 implements ICar {
@Override
public void run() {
System.out.print("賓士車開始運行...");
}
}
假設我們項目中有兩個類是依賴關係,此時我們只需要定義兩個抽象類就可以獨立開發了。
DIP第二種方法 構造函數傳遞依賴對象
package des.DIP;
//司機介面
public interface IDriver {
//司機可以駕駛汽車,什麼汽車不用管即抽象類(松耦合)
public void drive(ICar car);
/***************************/
public void drive();
}
package des.DIP;
public class Driver1 implements IDriver {
/******************************************************/
private ICar car;
//構造函數註入
public Driver1(ICar _car){
this.car = _car;
}
@Override
public void drive() {
this.car.run();
}
/******************************************************/
@Override
public void drive(ICar car) {
car.run();
}
}
IDriver ds1 = new Driver1(new Bmw1());
ds.run();
運行結果
構造函數依賴註入理解圖示
DIP第三種方法 setter方法傳遞依賴對象
代碼參考
package des.DIP;
//司機介面
public interface IDriver {
public void setCar(ICar car);
public void drive();
}
package des.DIP;
public class Driver1 implements IDriver {
/******************************************************/
private ICar car;
@Override
public void setCar(ICar car) {
this.car.run();
}
@Override
public void drive() {
this.car.run();
}
}
package des.DIP;
public class Client1 {
public static void main(String[] args){
IDriver ds1 = new Driver1();
ds1.setCar(new Bmw1());
ds1.drive();
}
}
DIP總結
- DIP本質就是通過抽象類來實現彼此獨立,互不影響
- 依賴倒置的核心是面向介面編程,即上面的第一種方法。
- 依賴倒置的具體使用規則如下
- 每個類儘量有介面或抽象類,或者二者都有。
- 變數的錶面類型儘量是介面或抽象類。
- 任何類不應該從具體類派生。
- 儘量不要覆寫基類的方法。
- 結合里氏替換原則進行。
- 依賴倒置需要審時度勢,而不是永遠抓住這個原則不放,任何一個原則的優點都是有限的。
對於倒置的理解
從反面講:什麼是正置?如上例子,我們開什麼型號的車,就依賴什麼樣型號的車。不存在什麼抽象類與介面,直接單獨建立即可,需要什麼建立什麼。但是依賴倒置?就是對車進行抽象,抽象出類和介面,建立抽象間的依賴。