六大設計原則(三)DIP依賴倒置原則
阿新 • • 發佈:2019-04-20
型號 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依賴倒置原則