元件內聚與耦合

★★★★Draft

FromClean Architecture

📚 From the Books

元件內聚與耦合軟體架構

如果說 SOLID 管的是「類別與函式」的層級,元件原則管的就是「可部署單位」的層級。元件是部署的最小單位——Java 的 .jar、Ruby 的 Gem、.NET 的 .dll。設計得當的元件應能獨立部署、獨立開發。這一層有六條原則,分成兩組:三條談「哪些類別該放在同一個元件裡」(內聚),三條談「元件之間該如何依賴」(耦合)。

🧠 Core Ideas

TIP

內聚三原則的落點會隨專案階段移動。專案初期「可發展性」最重要,重心偏向 CCP+CRP(在乎改得快,不急著被別人重用);專案後期其他團隊開始使用你的元件,重心才向 REP 移動(需要穩定的發佈管理)。好架構師不死守某一條原則——架構是關於時間與情境的動態平衡。

⚖️ 張力三角:顧此必然失彼

REP、CCP、CRP 三條內聚原則不是和諧共存,而是彼此拉扯。任何兩條被滿足,第三條就會被犧牲。

內聚三原則的取捨後果
  • REP + CCP(犧牲 CRP):元件太大,使用者被迫依賴一堆用不到的東西,帶來太多不必要的發佈。

  • CCP + CRP(犧牲 REP):元件太針對特定專案、缺乏版本管理,難以被其他團隊重用。

  • REP + CRP(犧牲 CCP):元件切得太細,一個簡單的需求變更卻要同時修改多個元件、到處救火。

  • 啟示:三角形沒有「正確答案」,只有「當前最適落點」。初期過度追求 REP(瘋狂切分+版本管理)會讓開發慢到死;後期還死守 CCP(把所有東西塞一起)則讓維護與重用變惡夢。

🖼️ 用數學丈量架構品質:主序列

耦合三原則裡最精采的,是 Uncle Bob 把「架構好不好」變成了可以計算的數字。他定義了兩個指標:不穩定性 I = FanOut /(FanIn + FanOut),衡量一個元件被依賴(穩定)還是依賴別人(不穩定);抽象性 A = 抽象類別數 / 總類別數。這裡的「穩定」不是「不變」,而是「難改變」——被越多人依賴的元件,改動它要付的代價越大,因此越難改、越穩定。

把 I 當 X 軸、A 當 Y 軸畫出象限,兩個角落是危險地帶:(0,0) 是「痛苦地帶」——高度穩定又高度具體,被大家依賴卻無法擴展,典型例子是資料庫 Schema 與具體 Utility 函式庫;(1,1) 是「無用地帶」——沒人依賴卻堆滿抽象介面,是過度設計。理想的元件應落在連接 (1,0) 與 (0,1) 的對角線「主序列」附近:越穩定就該越抽象(靠 OCP、用介面實現「難改但靈活」),越不穩定就該越具體。架構師的目標是讓每個元件到主序列的距離 D = |A + I − 1| 趨近於 0——這給了「架構品質」一個量化的數學依據。

IMPORTANT

元件結構不是專案第一天就設計好的,而是隨系統發展而演進。隨著功能增加,循環依賴會不斷冒出來,架構師必須持續監控並用 DIP(建介面反轉依賴)或抽出新元件來消除它。把類別層級的 SOLID 放大到可部署單位——這就是 SRP 變成 CCP、ISP 變成 CRP、DIP 變成打破環的工具的地方。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading