高層模組不應依賴低層模組,兩者都應依賴其抽象;
抽象不應依賴細節,細節應依賴抽象。
傳統設計中,高層模組(業務邏輯)直接依賴低層模組(具體實作):
// 高層模組直接依賴具體類別 Book
class Student {
public void read(Book book) {
System.out.println("學生開始閱讀:" + book.getName());
}
}
class Book {
public String getName() { return "書本"; }
}
當需求變更(如改閱讀 Website),必須修改 Student 類別,違反開閉原則,引入風險。
定義抽象介面 Reader,高層模組依賴抽象,低層模組實作抽象:
// 抽象介面
interface Reader {
String getName();
}
// 低層模組實作介面
class Book implements Reader {
public String getName() { return "書本"; }
}
class Website implements Reader {
public String getName() { return "網頁"; }
}
// 高層模組依賴抽象
interface Person {
void read(Reader reader);
}
class Student implements Person {
@Override
public void read(Reader reader) {
System.out.println("學生開始閱讀:" + reader.getName());
}
}
// 客戶端組裝
public class Test {
public static void main(String[] args) {
Person student = new Student();
student.read(new Book()); // 閱讀書本
student.read(new Website()); // 閱讀網頁,無需修改 Student
}
}
在 Spring 等框架中,DIP 透過控制反轉(IoC)容器落地:
@Service
public class StudentService {
private final ReaderRepository readerRepository; // 依賴介面
@Autowired // 建構子注入
public StudentService(ReaderRepository readerRepository) {
this.readerRepository = readerRepository;
}
}
容器負責在執行期注入具體實作(JpaReaderRepository、MockReaderRepository 等),實現編譯期完全解耦。
| 誤區 | 正確觀念 |
|---|---|
| 所有類別都要抽介面 | 僅針對「易變化」或「多實作」的依賴關係導入抽象 |
| DIP 等同於介面導向程式設計 | DIP 是架構原則,介面導向是落地手段之一 |
| 抽象層越多越好 | 過度抽象增加複雜度,需在彈性與簡潔間取得平衡 |
依賴倒轉原則是物件導向設計六大原則(SOLID)中最具架構指導意義的一條。它強迫我們關注「什麼」而非「如何」,透過抽象介面切斷具體實作的傳播鏈,構建出可測試、可替換、可演進的系統架構。實務中搭配 DI 容器使用,效果最佳。