Dependence Inversion Principle (DIP)
定義:
高層模組不應該相依於低層模組,兩者都應該相依於抽象。
抽象不應該相依於細節,細節應該相依於抽象。
說明:
傳統的軟體開發方法,如結構化分析與設計傾向於創造高階模組相依於低階模組。目標就是描述高階模組如何呼叫低階模組的子程式結構。
相較於以傳統程序化方法的相依結構,設計良好的物件導向程式的相依結構應該是反向的。
高層指的是依賴者 , 低層指的是被依賴者。如下圖
模組之間的依賴應該透過抽象,而不應該透過具體的方式(針對介面寫程式)
對象的引用盡量是抽象型態而不是具體型態。
因此使用DIP之後應該為
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{ ... }