本章介紹了六大套件設計原則(Package Principles),分為兩組:三個處理內聚性(Cohesion)的原則決定「哪些類別該放在同一個元件中」,三個處理耦合性(Coupling)的原則決定「元件之間該如何相互依賴」。最後引入一系列衡量指標,將這些原則量化。
元件內聚性原則#
REP:再利用/發布等價原則(Reuse/Release Equivalence Principle)#
The granule of reuse is the granule of release.
- 再利用的粒度就是發布的粒度:若希望某些類別能被其他人重用,它們就必須以正式發布流程來管理——有版本號、有追蹤系統、有完整的通知機制
- 使用者需要「選擇要不要升級到新版」的自由,因此作者必須承諾向後相容,並且提供足夠的通知期
- 推論:放在同一個元件中的類別必須是可以一起發布的,它們應共用同一套版本號與發布文件
重點: REP 原則意味著元件中的類別必須屬於一個可內聚重用的群組。不相關的類別不應放在同一個元件中——元件不是隨意將類別分桶的機制。
CCP:共同封閉原則(Common Closure Principle)#
The classes in a component should be closed together against the same kinds of changes. A change that affects a component affects all the classes in that component and no other components.
- CCP 是 SRP 在元件層級的對應:正如 SRP 說「一個類別不應該有多於一個理由需要修改」,CCP 說「一個元件不應該有多於一個理由需要修改」
- 將因相同原因而一起變化的類別聚集在同一個元件中,可以最小化發布、驗證與部署的工作量
- 可維護性的優先級高於可重用性——對多數應用程式而言,程式碼容易修改比容易重用更加重要
技巧: CCP 要求我們把因相同原因改變的類別放在一起。這意味著如果某個業務規則變更只需改動一個元件,那麼這個設計就是成功的。
CRP:共同重用原則(Common Reuse Principle)#
The classes in a component are reused together. If you reuse one of the classes in a component, you reuse them all.
- CRP 告訴我們哪些類別不該放在一起:如果元件中有些類別被使用,有些沒有,那麼未使用的類別變動時,使用者仍必須重新部署——這是不必要的耦合
- 緊密合作的類別(如容器類與其迭代器)應放在同一個元件中
- CRP 是 ISP 在元件層級的對應:ISP 說「不要依賴你不需要的方法」,CRP 說「不要依賴你不需要的類別」
注意: REP、CCP 與 CRP 三個原則之間存在張力。REP 與 CCP 傾向讓元件變大(把相關的類別聚在一起),CRP 則傾向讓元件變小(排除不被一起使用的類別)。架構師必須在三者之間取得平衡。
元件耦合性原則#
ADP:無環依賴原則(Acyclic Dependencies Principle)#
Allow no cycles in the component dependency graph.
- 當多個開發者在同一個專案中工作,若缺乏管理策略,就會出現晨間症候群(morning-after syndrome)——昨天能用的程式碼,今早因別人的修改而壞掉
- 兩種解決方案:
- 每週建置(Weekly Build):開發者各自獨立工作,週五整合。隨著專案成長,整合時間會侵蝕開發時間
- 消除依賴循環:將系統分割成可獨立發布的元件,每個團隊維護自己的元件並給予版本號

Figure 28.1: Component structures are a directed acyclic graph.
- 元件依賴圖必須是有向無環圖(DAG):當 A 依賴 B 時,不能存在一條路徑讓 B 回頭依賴 A
- 若出現循環依賴,可用兩種方式打破:

Figure 28.2: A component diagram with a cycle

Figure 28.3: Breaking the cycle with dependency inversion
- 依賴反轉原則(DIP):提取介面放在被依賴方的元件中
- 建立新元件:將兩個元件共同依賴的類別抽出,放入第三個元件
補充: 元件依賴圖不是在專案初期就能規劃好的——它會隨著系統成長與演化而改變。ADP 原則是在開發過程中持續監控並修正循環。
SDP:穩定依賴原則(Stable Dependencies Principle)#
Depend in the direction of stability.
- 穩定性的定義:一個元件如果有很多其他元件依賴它,就很難修改——它是穩定的。反之,如果一個元件不被任何人依賴,它就容易修改——它是不穩定的
- 穩定性並非「好」或「壞」——系統需要同時擁有穩定與不穩定的元件。關鍵是不穩定的元件不應被穩定的元件依賴

Figure 28.5: X: A stable component
- 穩定度指標(Instability, I):
- Ca(Afferent Coupling,傳入耦合):依賴這個元件中類別的外部類別數量
- Ce(Efferent Coupling,傳出耦合):這個元件中依賴外部類別的類別數量
- I = Ce / (Ca + Ce),範圍 [0, 1]
- I = 0 表示最大穩定,I = 1 表示最大不穩定

Figure 28.7: Tabulating Ca, Ce, and I

Figure 28.8: Ideal component configuration
- SDP 要求:元件的 I 值應沿著依賴方向遞減——每個元件的 I 值應小於它所依賴的元件的 I 值
- 若發現穩定的元件(低 I)依賴了不穩定的元件(高 I),可使用 DIP 反轉依賴方向

Figure 28.11: Fixing the stability violation, using DIP
SAP:穩定抽象原則(Stable Abstractions Principle)#
A component should be as abstract as it is stable.
- 穩定的元件既然難以修改,就應該透過抽象化來保持可擴展性:穩定的元件應包含大量抽象類別與介面,讓不穩定的元件去提供具體實作
- 抽象度指標(Abstractness, A):
- Na:元件中抽象類別與介面的數量
- Nc:元件中類別的總數
- A = Na / Nc,範圍 [0, 1]
主序列與距離指標#
- 結合 I(不穩定度)與 A(抽象度),可在 A-I 圖上繪製每個元件的位置
- 理想的元件應落在主序列(Main Sequence)上,即 A + I = 1 的直線上

Figure 28.13: Zones of exclusion
- 兩個應避免的區域:
- 痛苦區(Zone of Pain):A = 0, I = 0——既具體又穩定,修改極為困難。例如資料庫 schema 就常落在此區
- 無用區(Zone of Uselessness):A = 1, I = 1——完全抽象卻無人依賴,是死碼

Figure 28.14: Scatter plot of component D scores
- 距離指標(D, Distance from the Main Sequence):
- D = |A + I - 1|,範圍 [0, ~0.707]
- D 愈接近 0 表示元件愈接近主序列,是理想的設計
- 可將 D 標準化為 D’ = |A + I - 1|,範圍 [0, 1]
- 透過統計分析(平均值與標準差),可以找出偏離主序列過遠的異常元件
重點: 元件設計指標(I、A、D)是設計決策的輔助工具,而非絕對標準。它們幫助我們量化分析元件之間的依賴關係,找出可能有問題的設計,但最終的判斷仍需要架構師的專業經驗。
本章小結#
六大套件原則可總結如下:
| 原則 | 層級 | 關注重點 |
|---|---|---|
| REP | 內聚性 | 元件是可重用的發布單位 |
| CCP | 內聚性 | 因相同原因變化的類別放在一起 |
| CRP | 內聚性 | 不一起使用的類別不放在一起 |
| ADP | 耦合性 | 依賴圖不可有循環 |
| SDP | 耦合性 | 依賴方向朝向穩定 |
| SAP | 耦合性 | 穩定的元件應是抽象的 |
這些原則是 SOLID 原則在更高層級——元件與部署層級——的延伸,幫助架構師在可重用性、可維護性與穩定性之間做出合理的取捨。