談談軟體元件的內聚與耦合(1)

更新於 2022/12/29閱讀時間約 7 分鐘
近期花了一些時間研讀 Robert C. Martin 所著的 Clean Architecture 這本書,剛好看到了一些概念恰巧可以與工作上遇到的架構做一些印證,於是便想寫一些文章做一些紀錄。
在 Clean Architecture 中,提到了軟體元件的內聚與耦合的概念。

何謂內聚(Cohesion)與耦合(Coupling)呢?

在軟體工程(Software Engineering)中,內聚(Cohesion)與耦合(Coupling) 是軟體工程中,用來衡量元件的度量,我們可以用其來衡量模組與其他模組間的關連性或相依性。
內聚力(Cohesion) 顧名思義是指把相關的東西集合在一起,因此,模組本身不需依賴其他模組,就能完成工作。
當模組的內聚力越高,表示模組包含的物件或功能就越多。雖然提高了模組本身的獨立性,減少跟其他模組的耦合性,但也可能造成重覆程式碼,或違背單一職責原則(SRP)的情況發生。
至於耦合力(Coupling)則是該模組與其他模組或元件的相依程度的大小,耦合度越高,代表模組與其他元件有著越大的相依性。
在高耦合的情況下,很容易發生這種情況,你明明只是加了一個很小的需求,但是卻連帶影響到跟它有相依關係的部份,造成修改後,很多地方都出錯,反而需要要花額外時間去修正被影響的程式碼,「牽一髮動全身」,這句俗諺就很適合用來形容這種情形。
由上述敘述我們可以知道,高耦合將伴隨著低內聚,而高內聚則伴隨著低耦合,在軟體開發的世界中,高內聚的系統比高耦合的系統還要來得容易維護
到這裡,我們來頭看看在元件內聚性,則必須提到以下述三個原則,將是用來衡量元件內聚性的實際標準。

再使用性——發布等價原則 (REP)

再使用性等價原則其實相當好了解,除了「再使用」代表著我們應該要重複利用那些經常被使用到的模組或元件,或是要把相同類型的程式碼歸類在同一個元件中,被歸類在同一個元件中的類別和模組應該要一起被發佈(Release)並伴隨著對應的版號(Version number)。
同時間發佈同元件的模組和程式碼也代表著每次Release都是可追蹤的,把元件依版本號也有一個好處,我們可以透過模組管理工具來管理這些可以重複利用的元件,每次版號的更新耶可以看到對應的更改,常見的模組管理工具,例如 Java的Maven 及 Gradle、Node.js 的 npm、Python的 pip 等等。

共同封閉原則 (CCP)

共同封閉原則的意義在於,要將那些會因為相同理由、在相同時間發生變化的類別收集在相同的元件中,而將那些在不同時間、會因為不同理由產生變化的類別分割到不同元件之中。
實際上的意義其實類似於單一職責原則,每一個類別、元件、方法都應該只有一種理由需要改變(one reason to change),這麼做將可以避免不必要的重新部署、更改與重新驗證。
格蘭特這邊可以舉一個簡單的例子:
以下是一個員工 Employee 類別。
Class Employee {
public int calculateMonthlySalary() {...}
public int generateMonthlyHoursReport() {...}
public void saveEmployee() {...}
}
Employee 裡面有三個功能計算每月薪水產生每月工時報告、儲存Employee資料,那我們來看看有甚麼理由會需要更改這個類別呢?
1.會計部想改變時薪的計算方式->需要更改calculateMonthlySalary() 2.人資想改變加班計算方式->需要更改generateMonthlyHoursReport()要改 3.工程師想改變Employee的編碼方式->需要更改saveEmployee()
當今天如果人資想改變加班的計算方式,從原本的 8 小時之後算加班改成上滿 10 小時後才算加班,這個時候你可能會想,只要簡單改一改 generateMonthlyHoursReport() 裡的邏輯就好了。
不過,當改完後發現,哇咧,由於沒有區分好職責的關係,原本計算薪資的 calculateMonthlySalary()方法中呼叫了generateMonthlyHoursReport()方法,導致最後每一個員工的每月薪資都被超時計算了。
是不是很恐怖呢? 由以上的例子我們可以知道 CCP 的重要性,不同類型、會在不同時間觸發更改的方法應該獨立成不同的元件。
如果我們 Refactor 原本的類別成以下的樣貌,將可以避免上面的狀況﹔
class Employee {
private String id;
public String getId() {
return id;
  }
}
class PaymentService {
public int calculateMonthlySalary(Employee employee) {...}
}
class WorkHoursServiceService{
public HoursReport generateMonthlyHoursReport(Employee employee) {...}
}
class EmployeeDAO {
public void saveEmployee(Employee employee) {...}
}

共同重複使用原則 (CRP)

共同重複使用原則的意義在於「不要強迫元件的使用者依賴他們不需要的東西」。
試想,當今天你的元件依賴了一堆根本不需要的東西時,只要A發生改變,B就要跟著改,有用到A與B的元件也會需要跟著改變,而這些更改將伴隨著重新編譯、測試以及部署,這將會是一場浩劫啊。
因此,當我們依賴著某一個元件時,要確保我們依賴於該元件中的所有類別,意即該元件中的所有類別都是不可分割的。

元件內聚張力圖

不過.....我們有可能三種原則都遵守嗎?
答案是,不可能的,這三種原則將彼此作用,更像是一種Trade-Off(取捨)。
REP 和 CCP 是包容性原則 (Inclusive) 這兩個原則傾向於讓元件變更大,因為要內聚更多相同類別與方法。
而 CRP 是一個排除性原則 (Exclusive) 這個原則傾向讓元件變更小,要讓元件成為最小且不可分割的單位。
因此,如果太過注重 REP 和 CCP,架構就會需要太多沒必要的release,反之如果太過注重 CCP 和 CRP,那麼你的架構就會很難重複使用,而如果太過注重 CRP 和 REP,那麼每次更改都會動到很多元件。
就如同格蘭特工作上同時要接觸 Legacy code 與 設計良好的 code,如果在元件中內聚太多類別及方法,反而會造成一堆 release,而每次 release 都要花費對應的 Effort 去處理,但如果拆分過多元件的話,又會造成其之中的方法難以重複被利用,也因此,如果不好好考慮可維護性的話,legacy code就會變得越來越龐大。
所以,在衡量及設計實際的系統時,我們更應該需要審慎評估每個系統的需求,找出一個最符合的平衡點,就好像沒有100%的男人/女人,只有彼此磨合,找到雙方都可以接受的那種方法及樣貌。
今天有關元件內聚就先提到這裡,下一篇要來談的是元件耦合。
本文同步發表於格蘭特的部落格
avatar-img
14會員
17內容數
還在為不知道怎麼面試而煩惱嗎? 還在為苦無面試機會而沮喪嗎? 別擔心~讓我們一起面對! 在專題中,我將以自身經驗傳授如何撰寫履歷以及分享面試經驗。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
格蘭特的沙龍 的其他內容
在外地生活已近十年。 起初是因為念大學的關係,爾後繼續念了研究所,一直到現在,在異地工作已將近三年。
「待會睡醒要不要去爬山?」 手機傳來的是父親的訊息。 「好啊。」,我這麼回答。 灑滿陽光的午後,路上的行人彷彿因為口罩解禁的關係,洋溢著自在的氛圍。 午後的山上,樹木隨著秋風起舞,午後的陽光映照在坡旁的草皮上,如撒落一地的鑽石般耀眼。
國泰金控集團為國內五大民營金控之一(其餘為富邦金、國泰金、新光金、中信金及開發金)。 來看一下最近的營收狀況吧。 國泰金前11月稅後純益492.3億元、年減63.6%,累計每股稅後純益(EPS)雖減少至3.43元,但仍穩居14家上市金控每股純益第二高。
歡迎來到格蘭特的私房好書,本文是專題系列文的第一篇。 今天要與大家一同閱讀的是岩田松雄的《成為讓部屬願意追隨的上司-51個帶人先帶心的領導力》。
惠普(HP Inc.)身為全世界最大的電腦及辦公設備製造商,1993由威廉·惠利特(William Redington Hewlett)及大衛·普克德(David Packard)二位史丹佛大學畢業生於1939年創立,而公司名字 Hewlett-Packard 正是兩位創辦人名字的集合。
相信正在找工作的大家一定都是感到非常焦慮吧? 在進入正題之前,我想告訴正在迷茫的你幾句話
在外地生活已近十年。 起初是因為念大學的關係,爾後繼續念了研究所,一直到現在,在異地工作已將近三年。
「待會睡醒要不要去爬山?」 手機傳來的是父親的訊息。 「好啊。」,我這麼回答。 灑滿陽光的午後,路上的行人彷彿因為口罩解禁的關係,洋溢著自在的氛圍。 午後的山上,樹木隨著秋風起舞,午後的陽光映照在坡旁的草皮上,如撒落一地的鑽石般耀眼。
國泰金控集團為國內五大民營金控之一(其餘為富邦金、國泰金、新光金、中信金及開發金)。 來看一下最近的營收狀況吧。 國泰金前11月稅後純益492.3億元、年減63.6%,累計每股稅後純益(EPS)雖減少至3.43元,但仍穩居14家上市金控每股純益第二高。
歡迎來到格蘭特的私房好書,本文是專題系列文的第一篇。 今天要與大家一同閱讀的是岩田松雄的《成為讓部屬願意追隨的上司-51個帶人先帶心的領導力》。
惠普(HP Inc.)身為全世界最大的電腦及辦公設備製造商,1993由威廉·惠利特(William Redington Hewlett)及大衛·普克德(David Packard)二位史丹佛大學畢業生於1939年創立,而公司名字 Hewlett-Packard 正是兩位創辦人名字的集合。
相信正在找工作的大家一定都是感到非常焦慮吧? 在進入正題之前,我想告訴正在迷茫的你幾句話
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
Thumbnail
軟體工程師一直是一個高技術含量的工作,例如國外許多人工智慧大咖 更是各大科技巨頭搶著挖角的超級巨星,chatgpt出現後我也陸續做了一些實現,發現chatgpt確實聽得的我想做什麼甚至給出對應的code,無疑大大縮短了開發時間,甚至用字遣詞精確一點,完全用chatgpt寫code都有可能,這讓我開始
Thumbnail
會談這個主題主要是工作上預計進行Pair Programming的模型來開發,因而蒐集了一些關於Pair Programming這方面的相關概念與執行方向,並整理讓大家共同參考、討論。 🔔還沒成為Potato會員的朋友點這裡加入哦,撰寫文章還能挖礦打造被動收入 🔔 優點 提升程式碼品質 缺點
Thumbnail
前幾 天有一則新聞是說有一名工程師名為 augustgl 的工程師逆向工程 TikTok 的 Android 應用程式,並將所得的原始碼發至原始碼平台 Github 。文中augustgl 提到:「TikTok 是偽裝成社群媒體平台的數據收集引擎。它是合法的間諜軟體,因此我認為應逆向工程 Andro
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
Thumbnail
軟體工程師一直是一個高技術含量的工作,例如國外許多人工智慧大咖 更是各大科技巨頭搶著挖角的超級巨星,chatgpt出現後我也陸續做了一些實現,發現chatgpt確實聽得的我想做什麼甚至給出對應的code,無疑大大縮短了開發時間,甚至用字遣詞精確一點,完全用chatgpt寫code都有可能,這讓我開始
Thumbnail
會談這個主題主要是工作上預計進行Pair Programming的模型來開發,因而蒐集了一些關於Pair Programming這方面的相關概念與執行方向,並整理讓大家共同參考、討論。 🔔還沒成為Potato會員的朋友點這裡加入哦,撰寫文章還能挖礦打造被動收入 🔔 優點 提升程式碼品質 缺點
Thumbnail
前幾 天有一則新聞是說有一名工程師名為 augustgl 的工程師逆向工程 TikTok 的 Android 應用程式,並將所得的原始碼發至原始碼平台 Github 。文中augustgl 提到:「TikTok 是偽裝成社群媒體平台的數據收集引擎。它是合法的間諜軟體,因此我認為應逆向工程 Andro