1. 程式人生 > >六大設計原則(三)DIP依賴倒置原則

六大設計原則(三)DIP依賴倒置原則

型號 pre 面向接口 alt archive 但是 松耦合 實際類型 我們

原文:六大設計原則(三)DIP依賴倒置原則
依賴倒置原則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本質就是通過抽象類來實現彼此獨立,互不影響
  • 依賴倒置的核心是面向接口編程,即上面的第一種方法。
  • 依賴倒置的具體使用規則如下
    • 每個類盡量有接口或抽象類,或者二者都有。
    • 變量的表面類型盡量是接口或抽象類。
    • 任何類不應該從具體類派生。
    • 盡量不要覆寫基類的方法。
    • 結合裏氏替換原則進行。
  • 依賴倒置需要審時度勢,而不是永遠抓住這個原則不放,任何一個原則的優點都是有限的。

對於倒置的理解

從反面講:什麽是正置?如上例子,我們開什麽型號的車,就依賴什麽樣型號的車。不存在什麽抽象類與接口,直接單獨建立即可,需要什麽建立什麽。但是依賴倒置?就是對車進行抽象,抽象出類和接口,建立抽象間的依賴。

六大設計原則(三)DIP依賴倒置原則