我們該如何決定哪些類別(Classes)該被放在同個元件(.jar/.dll)裡?
過去可能憑感覺,把「功能相似」的放在一起。
但 Uncle Bob 提出,元件內聚性取決於三個原則,其核心精神是:「別依賴你不需要的東西。」

這三原則彼此間存在著強烈張力(Tension),架構師的工作就是在這三者間尋找動態平衡。

一、三大原則#

1. REP: 重用/發佈等價原則#

(The Reuse/Release Equivalence Principle)

「重用的粒度(Granularity)就是發佈的粒度。」

  • 核心: 如果希望別人重用你的元件,你須對該元件進行 發佈(Release) 管理
  • 理由: 使用者需知道版本號(如 v1.0, v1.1),才能放心重用。
    沒有版本號和變更紀錄,重用是不可能的
  • 結論: 被放在同個元件裡的類別,應具有相同的發佈策略與版本號

2. CCP: 共同封閉原則#

(The Common Closure Principle)

「將那些會因相同理由、在相同時間改變的類別,放在同個元件中。」

  • 對應原則: 這是元件層級的 SRP (單一職責原則)
  • 理由: 可維護性(Maintainability)。如果兩個類別總是同時修改
    (例如改了資料庫 schema 就得改對應Mapper),它們就該綁一起。
    這樣需求變更時,我們只需重新部署這個元件,而不用到處救火
  • 傾向: 讓元件變(包容性)

3. CRP: 共同重用原則#

(The Common Reuse Principle)

「不要強迫使用者依賴他們不需要的東西。」

  • 對應原則: 這是元件層級的 ISP (介面隔離原則)
  • 理由: 如果元件 A 依賴元件 B,即使只用了 B 中的一個類別,A 也會在物理上依賴 B 的所有東西。
    如果 B 裡面一個 A 沒用到的類別改了,A 還是得重新編譯/部署
  • 結論: 只有那些「幾乎總是同時被用」的類別,才該放在一起
  • 傾向: 讓元件變(排除性)

二、張力圖:架構師的平衡藝術#

這三原則不是和諧共存,它們彼此制衡

  • REP & CCP包容性原則:傾向將類別放一起(讓元件變大)
  • CRP排除性原則:傾向將類別拆開(讓元件變小)

Uncle Bob 畫了一個著名的「張力三角」:

組合忽略的原則導致的問題
REP + CCPCRP太多不必要發佈;元件太大,被迫依賴沒用東西
CCP + CRPREP難以重用;元件太針對特定專案,缺乏版本管理
REP + CRPCCP變更頻繁;簡單的需求變更需同時修改多個元件

三、結論:動態的平衡#

架構師的職責,就是根據專案當前狀態,在三角形中找到落點。

  • 專案初期: 可發展性(Developability) 最重要
    • 我們更在乎修改容易(CCP),而不是被別人重用(REP)
    • 重心偏向三角形右側(CCP + CRP)
  • 專案後期: 可重用性(Reusability) 變重要
    • 隨著專案成熟,其他團隊開始使用你的元件,你需要穩定的發佈管理
    • 重心逐漸向左側移動(REP)

好架構師不會死守某個原則。
如果你在專案初期就過度追求 REP(瘋狂切分元件並做版本管理),開發速度會慢到死;
如果你在專案後期還死守 CCP(把所有東西塞在一起),維護和重用將成為惡夢。
架構是關於時間與情境的動態平衡。