天天看點

前端必備的設計模式-依賴倒置原則

作者:前端餐廳ReTech

一. 什麼是依賴倒置原則

1.1 概念

依賴倒置原則(Dependence Inversion Principle, DIP), 其含義:

  • 高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象
  • 抽象不應該依賴細節, 細節應該依賴于抽象
  • 要針對接口程式設計,不要針對實作程式設計

1.2 什麼是依賴

這裡的依賴關系我們了解為UML關系中的依賴。簡單的說就是A use B,那麼A對B産生了依賴。具體請看下面的例子。

從上圖中我們可以發現, 類A中的方法a()裡面用到了類B, 其實這就是依賴關系, A依賴了B. 需要注意的是: 并不是說A中聲明了B就叫依賴, 如果引用了但是沒有真實調用方法, 那麼叫做零耦合關系. 如下圖:

前端必備的設計模式-依賴倒置原則

1.3 依賴的關系種類

1. 零耦合關系:如果兩個類之間沒有耦合關系,稱之為零耦合

前端必備的設計模式-依賴倒置原則

2. 直接耦合關系: 具體耦合發生在兩個具體類(可執行個體化的)之間,經由一個類對另一個類的直接引用造成。

前端必備的設計模式-依賴倒置原則

3. 抽象耦合關系: 抽象耦合關系發生在一個具體類和一個抽象類(或者接口)之間,使兩個必須發生關系的類之間存在最大的靈活性。

前端必備的設計模式-依賴倒置原則

依賴倒轉原則就是要針對接口程式設計,不要針對實作程式設計。這就是說,應當使用接口或者抽象類進行變量的類型聲明,參數的類型聲明,方法的傳回類型說明,以及資料類型的轉換等。

二. 依賴倒置的案例

2.1 初步設計方案

public class Benz {
    public void run() {
        System.out.println("奔馳跑起來了!");
    }
}

public class Driver {
    private String name;
    public Driver(String name) {
        this.name = name;
    }

    public void driver(Benz benz) {
        benz.run();
    }
}

public class CarTest {
    public static void main(String[] args) {
        Benz benz = new Benz();
        Driver driver = new Driver("張三");
        driver.driver(benz);
    }
}
複制代碼           

有一個駕駛員張三可以駕駛奔馳汽車, 于是最開始我們思考, 會有一個駕駛員類, 有一個奔馳汽車類. 随着業務的發展, 我們發現, 駕駛員張三還可以駕駛寶馬.

于是,我們定義一個BM類,

public class BM {
    public void run() {
        System.out.println("寶馬跑起來了!");
    }
}
複制代碼           

這時, 張三如果想要開寶馬, 就要将寶馬注冊在他名下.

public class Driver {
    private String name;
    public Driver(String name) {
        this.name = name;
    }

    public void driver(Benz benz) {
        benz.run();
    }

    public void driver(BM bm) {
        bm.run();
    }

}

public class CarTest {
    public static void main(String[] args) {
        Benz benz = new Benz();
        BM bm = new BM();
        Driver driver = new Driver("張三");
        driver.driver(benz);
        driver.driver(bm);
    }
}
複制代碼           

似乎這樣就可以了, 但是這樣有什麼問題呢?

  • 如果張三有一天要開大衆, 還要增加一個大衆車類, 同時還得挂載司機名下.
  • 不是所有的人都要開奔馳, 開寶馬. 開大衆.

這就是面向實作程式設計的問題, 接下來我們就要考慮面向接口程式設計.

2.2 改進後的方案

public interface ICar {
    public void run();
}

public class Benz implements ICar{
    public void run() {
        System.out.println("奔馳跑起來了!");
    }
}

public class BM implements ICar{
    public void run() {
        System.out.println("寶馬跑起來了!");
    }
}

public interface IDriver {
    public void driver(ICar car);
}

public class Driver implements IDriver{

    @Override
    public void driver(ICar car) {
        car.run();
    }
}

public class CarTest {
    public static void main(String[] args) {
        IDriver driver = new Driver();
        driver.driver(new Benz());
        driver.driver(new BM());
    }
}
複制代碼           

修改後的代碼, 提煉出來一個IDriver接口和ICar接口, 面向接口程式設計. IDriver的實作類駕駛員可以driver任何類型的汽車, 是以傳入參數也是一個接口ICar. 任何類型的汽車, 都可以通過實作ICar接口注冊為一種新的汽車類型. 當用戶端調用的時候, 将對應的汽車傳入就可以了.

三. 依賴的方式

3.1 依賴注入主要有三種方式:

1. 構造注入,在構造的時候注入依賴

在類中通過構造函數聲明依賴對象,按照依賴注入的說法,這種方式叫做構造函數注入,按照這種方式的注入,對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(); } 

}
複制代碼           

2. Setter方法注入

在抽象中設定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();
  } 

}

複制代碼           

3. 接口聲明依賴對象

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

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

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

3.2 依賴倒置原則在設計模式中的展現

  • 簡單工廠設計模式, 使用的是接口方法中注入
  • 政策設計模式: 在構造函數中注入.

繼續閱讀