A依賴B,B又反過來依賴A(或繞一圈間接回來)。兩者變成必須一起理解、一起改、一起測、無法各自重用的「連體嬰」。這是循環依賴——怎麼解開這個環?
🎯 Scenario
// SecureDocument 把自己整個傳給 DESEncryption
class SecureDocument {
fun encrypt(enc: DESEncryption): SecureDocument = enc.encrypt(this) // 傳 this
}
// DESEncryption 反過來認得 SecureDocument 的一切 —— 雙向相依
class DESEncryption {
fun encrypt(doc: SecureDocument): SecureDocument { /* 讀 doc 的內部 */ }
}
壞味道:違反無環相依原則(Acyclic Dependencies Principle)。改 A 影響 B、B 又繞回影響 A——連鎖效應從同一處出發又回到原點。書中真實案例是 java.util 六個抽象糾成一團「亂麻(tangle)」,大型間接環在複雜系統中幾乎肉眼看不見,是隱蔽 bug 的溫床。
🧠 Intuition
TIP
先別看下面。問自己:這個環裡,誰是「高階策略」、誰是「低階細節」?如果讓它們都去依賴一個抽象,而不是彼此的具體類別,箭頭會怎麼改向?(提示:把其中一條依賴反轉。)
⚖️ Reference Design
常見成因 — 對號入座
解法一:用 DIP 反轉一條依賴
// 讓『被依賴的一方』依賴由『使用方』定義的抽象,箭頭同向、環被打斷
interface Encryptable { fun bytes(): ByteArray } // 由 DESEncryption 這一側需要而定義
class DESEncryption {
fun encrypt(target: Encryptable): ByteArray { /* 只認得 Encryptable,不認得 SecureDocument */ }
}
class SecureDocument : Encryptable { // 細節依賴抽象
override fun bytes(): ByteArray = TODO()
fun encrypt(enc: DESEncryption) = enc.encrypt(this)
}- 依 DIP:抽出介面、把其中一條「具體 → 具體」的邊改成「具體 → 抽象」,環就被打斷。介面由需要它的那一側擁有(所有權反轉)。
解法二:只傳需要的東西,別傳整個 this
🔑 Takeaways
- 循環依賴違反無環相依原則:環裡的抽象被迫一起改、一起測、無法各自重用。
- 首選解法是 DIP——抽出介面、反轉一條邊的方向打斷環。
- 別隨手傳
this(守迪米特法則);對稱回呼用 Observer 解對稱。
No notes yet — jot your takeaways or Q&A here.