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

閱讀時間約 10 分鐘
本篇文章將會繼續上一篇的內容,還沒看過上一篇的朋友,記得先看完上一篇再繼續本篇的閱讀唷。
前一篇我們提到了元件的內聚,以及用來衡量內聚性的三個標準,CCP、CRP與REP。
在這篇中,我們要來繼續談談軟體的耦合性。
元件的耦合是指元件與其他模組或元件之間的相依性,越大的話代表該元件與其他元件的相依性越大。
接下來在元件的耦合性中,以下這三個原則則是用來處理元件之間的關係。

無環依賴原則(AOP)

無環依賴原則的意義在於,在元件的依賴圖關係中不允許出現環
乍聽之下,似乎有點抽象,甚麼是環呢?
讓我們來看一下這張圖﹔
在右下角處,Authorizer、Interactors、Entities 形成了一個閉鎖的環狀依賴,我們就稱這個狀況為依賴環。
箭頭的方向也是有意義的,尖的地方代表被依賴者,沒有尖的地方則代表依賴者,舉例來說,Entities 中用到了 Authorizer 中的類別,所以方向會朝向Authorizer。
不過,依賴環會產生甚麼問題呢?
如果要測試 Entities 元件的話,我們必須要建置 Authorizer 和 Interactors,由於它是環狀依賴,使其很難確定元件建置的順序,這種環也使得我們非常難以對元件進行隔離。
如此以來,「隔天早上綜合症(Morning-after syndrome)」便會很常發生,當你今天下班前開心的把某個元件的 change 推上 code repo,結果隔天來發現你的系統突然壞了,原因是因為前一天還有人比你晚走,並改了你所依賴的某個函式庫。

解除依賴環

要解除元件之間的依賴環,並把其恢復為有像無環圖,可以使用下面這兩種方法。
依賴反向原則(Dependency-Inversion Principle) :
根據上面的情況,假設在 Entities 中的 User 類別用到了 Authorizer 中的類別,我們可以建立一個具有 User 所需物件的介面,並把其放入 Entities 中,讓Authorizer 中的該類別繼承它,由此而來便反向了 Entities 與 Authorizer 之間的依賴關係,解除了依賴環。
新建立一個 Entities 和 Authorizer 都依賴的元件,把 Entities 和 Authorizer 都依賴的類別移動到這個元件中。
兩種方法個人覺得其實概念上相當接近,都是想反轉 Authorzer 與 Entities 之間的依賴關係,差別在於是否要把依賴的元件獨立出來,這就回到了上一篇所講的元件內聚性,我們在評估反轉依賴時就要考量,是否有必要把元件獨立出來呢? 亦或者把介面抽離出來即可?
讀到這裡,我們就必須要了解:
  • High level modules should not depend upon low level modules. Both should depend upon abstrations. (高階模組不能相依於低階模組,而兩者都要相依於抽象)
  • Abstractions should not depend upon details. Details should depend upon abstractions. (抽象不可以相依於細節,而細節必須相依於抽象)
通常在做系統分析與設計時,當看系統的高度升高時,就能看到各元件的流程,再升高一點,就能看到系統之間的整合方式,再升高一點,就能看到整個系統的架構,所謂的高階 (High level),是指以系統分析的高度,將架構內各個具體的細節加以抽象化到較高的位置時,它就是高階物件,而當元件開始擺脫細節的時候,設計者就會抽絲剝繭將共同的特性抽出來,這時設計的位階就會逐步增加,當增加到不能再增加的時候,抽象就形成了。
在抽象的形成過程中,系統分析師基本上會檢視要抽象的成員抽出的程度,據以調整細節設計,因此細節會自然的相依於抽象,這些細節的部份的位階就會較低,因此也被稱為低階 (Low level) 物件,這類物件通常稱為具體類別 (Concrete Class)。
有興趣的讀者可以參考這篇文章,有更多的敘述。
看了一堆文字敘述,我想大家應該頭有點昏吧,這裡,Grant 也準備了一些例子。
public class ReportStatistic() {     
  private SQLAccess _access = new SQLAccess();          
  public double Sum() {         
  var items = _access.GetAllCost();         
    ...     
  } }  
  public class SQLAccess {     
  public List<CostItem> GetAllCost() {  
       ...     
  } 
}
在 ReportStatistic 方法中,我們可以看到整個系統的流程,代表著它就是高階模組,而我們也看到了 ReportStatstic 方法與負責實作 SQL 存取的細節方法 SQLAccess 方法有著緊密的相依性。
當今天系統規模一變大,很可能這種高耦合的狀況就會變得更加嚴重,一旦改了低階模組,連帶高階模組都必須要受到更動。
因此,在類別中,不應該直接使用另一個具有實作類別,而是要依賴抽象的介面,去承接繼承該介面的實作類別。它的目標就是解除物件與物件間,兩者的直接相依關係。
如果將上面的ReportStatistic方法改為依賴於抽象,就會變得好維護許多。
public class ReportStatistic()
{
private IAccess _access = new SQLAccess();

public double Sum()
{
var items = _access.GetAllCost();
...
}
}

public interfalce IAccess
{
List<CostItem GetAllCost();
}

public class SQLAccess : IAccess
{
public List<CostItem> GetAllCost()
{
...
}
}

穩定依賴原則(SDP)

穩定依賴原則的意義在於我們要讓依賴關係朝著穩定的方向進行依賴。
之前提及的共同封閉原則中,我們提及某些元件被設計成是可變的,某些則是必須要被封閉的,只因為沒有 100% 的封閉,但我們不想要在上change的同時,所有的元件都會受到影響。
因此,對於任何元件而言,如果預期它是可變的,就不應該也不要讓一個難以更改的元件依賴它。

元件穩定性

不過,我們要如何去評估一個元件是不是穩定的呢?
一個可行的方法是「讓許多元件依賴它」,一個穩定的元件是相對不容易被改變的,同時也必須承擔被依賴的責任。
針對下圖中的 X,我們便可以稱其為穩定的元件,因為它對三個元件具有責任,而 X 本身不依賴任何元件,也代表著任何外部因素都不會讓其改變。
同樣的,我們也來看看甚麼是不穩定的元件。
下圖中的 Y 依賴三個外部元件,且本身不被任何元件依賴,不用負擔任何責任。

計算元件穩定性的度量

  • Fan-In: 輸入依賴度,代表說多少外部元件依賴此元件內部的類別
  • Fan-Out: 輸出依賴度,代表多少此元件內部的類別依賴於別人
  • Instability: 不穩定性 I = (FO)/(FI+FO) ,介於[0,1],0代表非常穩定 1代表非常不穩定,代表沒有任何元件依賴於該元件
計算元件穩定性的方式也很簡單,下圖中的Cc的穩定性為 1/(1+3) = 1/4。
不過,實際在設計系統時,我們不會希望所有的元件都是穩定的,我們希望有些元件是不穩定、有些是穩定的,而穩定與不穩定的元件的配額就必須要參照實際情況來決定了。

穩定抽象原則(SAP)

穩定抽象原則秉持著「一個穩定的元件也應該是抽象的」,這樣它的穩定性就不會影響它擴展。
會這樣是因為雖然我們不希望穩定模組經常被改變,不過還是有些特殊情形,我們希望穩定元件可以以穩定的方式進行擴展。
SAP 規定穩定性意味著「抽象性」,因此,依賴應朝向「抽象」的方向進行。
若定義了不穩定性I 以及抽象性A(Na/Nc) ,我們可以建立一個以I為橫軸 A為縱軸的座標圖:
Na(抽象類別及介面的總數)
Nc(類別總數)
最穩定的地方是座標圖的左上,最不穩定的地方是右下,但要注意的是,並不是所有元件都只能落在這兩個地方,因為元件的抽象性跟依賴性有程度之分,例如,抽象類別也可能依賴於其他的抽象類別。
但即便如此,還是有一些是元件不應該在的地方,分別是右上角的無用地帶,以及左下角的痛苦地帶。

無用地帶

若落於這個地方,代表元件有著最大的抽象,但卻沒有任何人依賴它,若無來依賴於它,那根本毫無用武之地。

痛苦地帶

在這裡的元件是具有高度穩定且具體的元件,我們不想要這種元件,因為其太具體所以很難進行擴展。
因此,盡可能避免落於無用以及痛苦地帶後,元件便會落於主序列上(Main Sequence)。
在主序列上的元件既不是太穩定、也並非太抽象,而最佳的端點則落於主序列的兩個頂點上,但這終究是不太可能達成的,落於主序列上的元件已經算是很不錯了。
最後,我們也許也有可能會希望微調目前系統上的元件,這個時候就必須要評估元件到主序列的距離,然後對其進行調整。

總結

這章的內容對於軟體及系統架構的設計非常有幫助,我們可以透過這些實際度量的準則設計未來的系統或評估目前的系統,看到這些內容,Grant也不禁恍然大悟,有時候在工作上,較資深的工程師會提點我們要小心依賴的方向,以及嚴格控管依賴,Grant 之前也許在不經意之中,讓系統形成了依賴環。
因此,在撰寫程式碼或設計系統前,也許考慮的多一點,就可以讓日後的維護變得相對輕鬆一點。
本文同步發表於格蘭特的部落格
參考資料
avatar-img
14會員
17內容數
還在為不知道怎麼面試而煩惱嗎? 還在為苦無面試機會而沮喪嗎? 別擔心~讓我們一起面對! 在專題中,我將以自身經驗傳授如何撰寫履歷以及分享面試經驗。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
格蘭特的沙龍 的其他內容
嗨! 我是格蘭特,歡迎來到軟體工程師面試大哉問! 近二十家公司面試經驗大公開,本篇是系列文的第四篇。 本篇要分享的是曾經極富盛名的消費性電子產品公司 HTC(宏達電)旗下子公司DeepQ 的面試經驗。
近期花了一些時間研讀 Robert C. Martin 所著的 Clean Architecture 這本書,剛好看到了一些概念恰巧可以與工作上遇到的架構做一些印證,於是便想寫一些文章做一些紀錄。 在 Clean Architecture 中,提到了軟體元件的內聚與耦合的概念。
在外地生活已近十年。 起初是因為念大學的關係,爾後繼續念了研究所,一直到現在,在異地工作已將近三年。
「待會睡醒要不要去爬山?」 手機傳來的是父親的訊息。 「好啊。」,我這麼回答。 灑滿陽光的午後,路上的行人彷彿因為口罩解禁的關係,洋溢著自在的氛圍。 午後的山上,樹木隨著秋風起舞,午後的陽光映照在坡旁的草皮上,如撒落一地的鑽石般耀眼。
國泰金控集團為國內五大民營金控之一(其餘為富邦金、國泰金、新光金、中信金及開發金)。 來看一下最近的營收狀況吧。 國泰金前11月稅後純益492.3億元、年減63.6%,累計每股稅後純益(EPS)雖減少至3.43元,但仍穩居14家上市金控每股純益第二高。
歡迎來到格蘭特的私房好書,本文是專題系列文的第一篇。 今天要與大家一同閱讀的是岩田松雄的《成為讓部屬願意追隨的上司-51個帶人先帶心的領導力》。
嗨! 我是格蘭特,歡迎來到軟體工程師面試大哉問! 近二十家公司面試經驗大公開,本篇是系列文的第四篇。 本篇要分享的是曾經極富盛名的消費性電子產品公司 HTC(宏達電)旗下子公司DeepQ 的面試經驗。
近期花了一些時間研讀 Robert C. Martin 所著的 Clean Architecture 這本書,剛好看到了一些概念恰巧可以與工作上遇到的架構做一些印證,於是便想寫一些文章做一些紀錄。 在 Clean Architecture 中,提到了軟體元件的內聚與耦合的概念。
在外地生活已近十年。 起初是因為念大學的關係,爾後繼續念了研究所,一直到現在,在異地工作已將近三年。
「待會睡醒要不要去爬山?」 手機傳來的是父親的訊息。 「好啊。」,我這麼回答。 灑滿陽光的午後,路上的行人彷彿因為口罩解禁的關係,洋溢著自在的氛圍。 午後的山上,樹木隨著秋風起舞,午後的陽光映照在坡旁的草皮上,如撒落一地的鑽石般耀眼。
國泰金控集團為國內五大民營金控之一(其餘為富邦金、國泰金、新光金、中信金及開發金)。 來看一下最近的營收狀況吧。 國泰金前11月稅後純益492.3億元、年減63.6%,累計每股稅後純益(EPS)雖減少至3.43元,但仍穩居14家上市金控每股純益第二高。
歡迎來到格蘭特的私房好書,本文是專題系列文的第一篇。 今天要與大家一同閱讀的是岩田松雄的《成為讓部屬願意追隨的上司-51個帶人先帶心的領導力》。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
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
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
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