什麼是高內聚與低耦合?
高內聚 (High Cohesion) 與 低耦合 (Low Coupling) 是評估軟體架構優劣的黃金準則。內聚 指的是模組內各元素彼此關聯的緊密程度,高品質的元件應確保「每個部分都為了同一個目標工作」;耦合 則是模組間相互依賴的程度。理想的設計應追求「對內緊密、對外獨立」:將相關的功能、資料與邏輯封裝在一起(高內聚),並透過明確的介面與其他模組互動,減少彼此間的直接牽連(低耦合)。這不僅能降低因修改一處而引發全身的風險,更能大幅提升程式碼的重用性與可維護性,讓系統在需求變更時具備更強的適應力。
不知道該怎麼辦的衣服褲子就披在椅背上,有一天,椅子就倒了。這不僅是生活中的混亂,也是許多軟體專案在缺乏 程式碼組織 規劃時的寫照。
分類是為了讓物品達到秩序與平衡的一門藝術。
檔案分類:屬性導向 vs 功能導向
在建立 軟體架構設計 時,我們常面臨分類方式的選擇。這就像整理衣櫃,一個衣櫃就像生活的一面鏡子,反映出選擇和價值觀:
1. 依屬性分類 (Horizontal Layering)
將所有的內衣放一起、褲子放一起。對應到程式碼,就是常見的 components/, utils/, services/ 目錄結構。這種做法的優點是簡單直覺,但在大型專案中,要找齊一個功能相關的所有檔案會變得很困難。
屬性分類用衣服的概念來看就是分類成:
- 全部的內衣
- 全部的內褲
- 全部的褲子
- 全部的上衣
- 全部的外套
2. 依功能分類 (Vertical Slicing)
將慢跑外套、運動褲、運動內衣放在同一格。對應到程式碼,就是 features/user/, features/checkout/。這能提升開發效率,因為功能相關的程式碼在物理距離上是靠近的。
功能分類就會是:
- 慢跑外套、上衣、褲子、內衣、內褲
- 登山外套、上衣、褲子、內衣、內褲
- 約會外套、上衣、褲子、內衣、內褲
- 上班外套、上衣、褲子、內衣、內褲
- 居家外套、上衣、褲子、內衣、內褲
- 休閒外套、上衣、褲子、內衣、內褲
分類衣物同時也為了簡化生活和尊重資源,每一件衣物都有其獨特性,就像生命中的每一個瞬間,我們的目標是不斷尋找平衡,在分類的過程中幫助我們更好地理解自己,無論是在衣櫃中還是生活中。
Cohesion (內聚) 與 Coupling (耦合)
在 模組化設計 中,這兩個概念是評判架構優劣的黃金標準:
- Cohesion (內聚): 簡單來說,就是把相關的東西放在一起。高品質的元件應該是高內聚的,每個部分都為了同一個目標工作。若分類不當,相關性低的東西強行放在一起,那不叫內聚,而叫「垃圾桶」。
- Coupling (耦合): 描述模組之間的依賴程度。如果你出門前需要分別從五個不同的抽屜找到零件才能穿好一套衣服,這就是高耦合。
實戰建議:概念耦合 vs 實作耦合
要做到完全「去耦合」是不可能的,但我們可以區分不同的層次:
- 概念上耦合: 衣服跟褲子在「穿搭」的概念上是相關的。這在程式碼中體現為介面或合約。
- 實作上耦合: 衣服的鈕扣必須剛好扣在褲子的扣眼上。這在程式碼中應盡量避免,否則修改其中一方就會破壞另一方。
舉三個例子來看看:
- 如果不在意眼光,其實也可以穿 Polo 衫去運動或是穿著運動褲去上班,衣服跟褲子只有在概念上進行耦合。
- 車上的導航機來說只有在裝機的當下耦合,原廠車機大部分都能改裝成第三方廠商有更多功能的版本,僅需要轉接頭來配合不同廠牌。
- 宗教透過精神上讓大家可以因為一個概念而聚集是好事,但如果要求雙修在肉體上進行 "耦合" 就會出現問題。
專案實務:如何選擇目錄結構?
在現代的前端開發中,我們傾向於「混合模式」:
- 底層基礎件(如 Button, Input)採用「屬性分類」,存放在
src/components中,確保高度重用(高內聚)。 - 業務功能塊(如 UserProfile, OrderList)採用「功能分類」,存放在
src/features中,將相關的 API、Hook、UI 封裝在一起(低耦合)。
分類之道最終是在追求物品的秩序與平衡,無論是檔案組織、功能拆分還是錯誤處理,良好的分類都能讓系統具備更高的維護性,幫助我們在開發過程中掌握主動權,實現早點下班的目標。
FAQ:高內聚低耦合常見問題
Q1:如果我發現我的目錄裡出現了 common/ 或 shared/ 垃圾桶,該怎麼辦?
A:這是極其常見的現象。一旦 shared/ 目錄大到難以尋找,請立即啟動「斷捨離」。檢查裡面的檔案,如果是特定功能(如 formatUser)請移至對應的功能目錄;如果是真的底層通用邏輯(如 formatDate),則應細分至屬性分類(如 src/utils/date/)。
Q2:高內聚會不會導致單個檔案過大?
A:高內聚並不代表要寫在同一個檔案裡。高內聚是指「邏輯上的緊密性」。您可以透過分拆檔案,但將它們放置在同一個資料夾(如 src/features/auth/)下,包含 api.ts, hooks.ts, component.tsx。這樣既維持了功能上的高度凝聚,又保持了程式碼的整潔。
Q3:如何判斷耦合度是否過高?
A:一個經典的「程式碼異味」是:當您修改 A 檔案的參數名稱,卻發現有 10 個不相干的 B, C, D 檔案報錯。這表示它們之間存在嚴重的實作耦合。理想情況下,您應該透過介面(Interface)或合約進行「概念耦合」,只要介面不變,內部的修改不應影響他人。
喜歡這篇文章,請幫忙拍拍手喔 🤣