Dependence Inversion Principle (DIP)

 

定義:

高層模組不應該相依於低層模組,兩者都應該相依於抽象。
抽象不應該相依於細節,細節應該相依於抽象。
 

說明:

傳統的軟體開發方法,如結構化分析與設計傾向於創造高階模組相依於低階模組。目標就是描述高階模組如何呼叫低階模組的子程式結構
相較於以傳統程序化方法的相依結構,設計良好的物件導向程式的相依結構應該是反向的
高層指的是依賴者 , 低層指的是被依賴者。如下圖
dip_before
模組之間的依賴應該透過抽象,而不應該透過具體的方式(針對介面寫程式)
對象的引用盡量是抽象型態而不是具體型態
因此使用DIP之後應該為
dip_after
HighLevel 和 LowLevel 都相依於 abstract level。
 

注意:

若是具體類別已經相當穩定,不太會變化,依賴於該具體類別也是無妨
 
Example Code:
以先前的 LSP 的範例 來說明

public interface Vehicle {
    public void drive();
}
public class Bike implements Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
}
public class Car implements Vehicle{
    public void drive(){
        System.out.println("drive car");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

若用一個 Man 來代表使用者,並讓 Man 可以操縱 Bike,在未加考慮的情況會直接寫出以下 code,並違反了 DIP。
在 driveBike 的方法參數,並沒有依賴抽象(Vehicle),相反的依賴具體型態(Bike)。

public class Man {
    public void driveBike(Bike bike){
        bike.drive();
    }
}

修改的方法很簡單,把參數型態改為 Vehicle。
另一個依賴抽象的好處是可以減少多餘的code,在依賴具體 Bike 的情況下, 若我們想讓 Man 可以駕駛 Car,  必須還要寫出另一個 driveCar 方法。

public class Man {
    public void driveBike(Bike bike){
        bike.drive();
    }
    public void driveCar(Car car){
        car.drive();
    }
}

修改 Man 的 driveVehicle 方法,讓其參數型態依賴抽象(Vehicle)。

public class Man {
    public void driveVehicle(Vehicle vehicle){
        vehicle.drive();
    }
}

調用端呼叫 driveVehicle 方法可以根據傳入參數型態的不同,具有不同的行為。

public class Main {
    public static void main(String[] args) {
        Man man = new Man();
        man.driveVehicle(new Bike());
        man.driveVehicle(new Car());
    }
}

 
另一種依賴具體的形式為基本型別。如

public int getSummary(int a, int b)
{
...
}

如果需要支援多種 Summary 算法,可以引入抽象層為參數。

public int getSummary(CustomType a, CustomType b){
...
}
public interface CustomType{
...
}