天天看點

設計模式—— 三:依賴倒置原則什麼是依賴倒置原則?為什麼要用依賴倒置原則?依賴的三種寫法

文章目錄

先看看依賴倒置原則的原始定義:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions

翻譯一下,包含三層定義:

● 高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象;

● 抽象不應該依賴細節;

● 細節應該依賴抽象。

高層子產品和低層子產品容易了解,每一個邏輯的實作都是由原子邏輯組成的,不可分割的原子邏輯就是低層子產品,原子邏輯的再組裝就是高層子產品。

抽象和細節呢?在Java語言中,抽象就是指接口或抽象類,兩者都是不能直接被執行個體化的;細節就是實作類,實作接口或繼承抽象類而産生的類就是細節,其特點就是可以直接被執行個體化。

依賴倒置原則在Java語言中的表現就是:

● 子產品間的依賴通過抽象發生,實作類之間不發生直接的依賴關系,其依賴關系是通過 接口或抽象類産生的;

● 接口或抽象類不依賴于實作類;

● 實作類依賴接口或抽象類。

采用依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,降低并行開發引起的風險,提高代碼的可讀性和可維護性。

以司機駕駛奔馳車為例:

3-1:司機駕駛奔馳車類圖

設計模式—— 三:依賴倒置原則什麼是依賴倒置原則?為什麼要用依賴倒置原則?依賴的三種寫法

奔馳車提供一個方法run,代表車輛運作。

奔馳車類:

public class Benz { 
 //汽車會跑 
  public void run(){ 
    System.out.println("奔馳汽車開始運作..."); 
   }
}      

司機通過調用奔馳車的run方法開動奔馳車.

司機類:

public class Driver { 
  //司機的主要職責就是駕駛汽車
   public void drive(Benz benz){ 
   benz.run();
   }
 }      

有車,有司機,在Client場景類産生相應的對象.

場景類:

public class Client { 
  public static void main(String[] args) { 
    Driver zhangSan = new Driver();
    Benz benz = new Benz(); //張三開奔馳車 
    zhangSan.drive(benz);
   } 
 }      

以上實作了司機開車的場景,在功能上是沒有問題。

現在新的需求來了,司機不僅要開奔馳車,還要開寶馬車。

寶馬車類:

public class BMW { 
  //寶馬車當然也可以開動了
   public void run(){ 
     System.out.println("寶馬汽車開始運作..."); 
    }
 }      

現在發現問題了,司機沒有開動寶馬車類的方法,怎麼辦?給司機類添加開寶馬車的方法嗎?如果接下來司機要開挖掘機呢?在這裡,司機類和奔馳車類之間是緊耦合的關系,其導緻的結果就是系統的可維護性大大降低。

這時候就應該引入依賴倒置原則。

3-2:引入依賴倒置原則後的類圖

設計模式—— 三:依賴倒置原則什麼是依賴倒置原則?為什麼要用依賴倒置原則?依賴的三種寫法

建立兩個接口:IDriver和ICar,分别定義了司機和汽車的各個職能,司機實作現drive()方法。

司機接口:

public interface IDriver { 
 //老司機,會開車
  public void drive(ICar car); 
}      

司機實作類:實作IDriver接口:

public class Driver implements IDriver{
  //司機的主要職責就是駕駛汽車 
  public void drive(ICar car){
     car.run();
 } 
}      

在IDriver中,通過傳入ICar接口實作了抽象之間的依賴關系,Driver實作類也傳入了ICar 接口,至于到底是哪個型号的Car,需要在高層子產品中聲明。

汽車接口及兩個實作類:

public interface ICar { 
 public void run();
 }

//奔馳汽車類
public class Benz implements ICar{ 
  public void run(){ 
  System.out.println("奔馳汽車開始運作...");
 } 
}

//寶馬汽車類
public class BMW implements ICar{ 
  public void run(){ 
    System.out.println("寶馬汽車開始運作...");
  } 
}      

業務場景類:

public class Client { 
  public static void main(String[] args) { 
    IDriver zhangSan = new Driver(); 

    ICar benz = new Benz();
    //張三開奔馳車
     zhangSan.drive(benz); 

    ICar bmw = new BMW(); 
    //張三開寶馬車
    zhangSan.drive(bmw);

   } 

}      

在新增加低層子產品時,隻修改了業務場景類,也就是高層子產品,對其他低層子產品如 Driver類不需要做任何修改,業務就可以運作,把“變更”引起的風險擴散降到最低。

依賴是可以傳遞的,A對象依賴B對象,B又依賴C,C又依賴D——隻要做到抽象依賴,即使是多層的依賴傳遞也是沒有絲毫問題的。

對象的依賴關系有三種方式來傳遞,如下所示:

在類中通過構造函數聲明依賴對象,按照依賴注入的說法,這種方式叫做構造函數注入,按照這種方式的注入,對IDriver和Driver進行修改。

public interface IDriver { 
  //司機就會開車
   public void drive(); 
}

public class Driver implements IDriver{
  private ICar car; 
  //構造函數注入 
  public Driver(ICar _car){ 
    this.car = _car; 
  }
  //司機的主要職責就是駕駛汽車 
  public void drive(){ 
    this.car.run(); 
  } 

}      

在抽象中設定Setter方法聲明依賴關系,依照依賴注入的說法,這是Setter依賴注入,按照這種方式的注入,對IDriver和Driver進行修改:

public interface IDriver { 
  //車輛型号 
  public void setCar(ICar car);
 //是司機就應該會駕駛汽車
  public void drive(); 
}

public class Driver implements IDriver{
  private ICar car; 
  public void setCar(ICar car){ 
  this.car = car; 
  }

 //司機的主要職責就是駕駛汽車
 public void drive(){ 
   this.car.run();
  } 

}      

在接口的方法中聲明依賴對象,未修改的IDriver和Driver就采用了接口聲明依賴的方式,該方法也叫做接口注入。

public interface IDriver { 
 //老司機,會開車
  public void drive(ICar car); 
}

public class Driver implements IDriver{
   //司機的主要職責就是駕駛汽車 
   public void drive(ICar car){ 
     car.run(); 
   } 
}