一. 什麼是依賴倒置原則
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 依賴倒置原則在設計模式中的展現
- 簡單工廠設計模式, 使用的是接口方法中注入
- 政策設計模式: 在構造函數中注入.