細談元件耦合性
March 27, 2020這篇文章討論 Clean Architecture 裡面的元件耦合性章節
圖片以及程式碼來源自Clean Architecture
耦合性
討論完了元件內部的內聚性 這篇文章來聊聊元件跟元件之間的關係 稱為耦合性(Coupling)
我們也曾經提到過 Coupling這個概念
本章再提出三個原則 來細談元件之間的耦合性 分別是
1.無環依賴原則
2.穩定依賴原則
3.穩定抽象原則
最後 我們會討論怎麼計算一個元件有沒有遵循這些原則 並給予量化
無環依賴原則 The Acyclic Dependencies Principle
Allow no circles in the component dependency graph
當我們將元件按照 deploy單位 來劃分時 每一個單獨的元件 可以由一個單獨的團隊來開發 當你要加新的功能 就把自己負責的元件改動之後 賦予一個新的版本號 讓使用你的人決定要不要使用或是什麼時候使用
這麼一來 就沒有一個團隊被另外一個團隊完全支配 對於一個元件的修改不必立即的反映到另一個元件上
這是一個簡單的合理的開發程序 但要成功執行 就必須對元件的依賴進行管理 元件的依賴中不能出現環
看個書上的例子
無論從哪個元件開始 都無法按照箭頭走回同一個元件
這個有向圖非常清楚 比如說今天Presenters
有了新版本 那就只有Main
跟View
需要仔細考慮他們何時需要跟Presenters
整合
要發佈整個系統時 是從下而上發佈 也就是先編譯測試發佈Entities
一路下去最後再發佈Main
環來了
要是今天來了一個環 Entities
指到Authorizer
慘了 那今天要發佈Entities
之前 要先確定Authorizer
發佈成功 但Authorizer
又要先等Interactor
先成功 Interactor
又要先等Entities
那就沒完沒了 發佈的時間上升 複雜度上升 測試難度也上升
解開環
解法一: DIP
我們把User
真正需要呼叫到的方法 用一個抽象分離出來 讓Authorizer
實作這個抽象
這樣箭頭反向 依賴圖裡的環就不見了
解法二: 新開一個Entity
跟Authorizer
都共同依賴的元件
穩定依賴原則 The Stable Dependencies Principle
Depend in the direction of stability.
朝著穩定的方向進行依賴
什麼意思呢 如果一個元件他很可能會被變動 那我們就不要讓一個很難以改動的元件去依賴它 否則那個很可能會被變動的元件也會很難改動
穩定性
稍微定義一下專有名詞
下圖是個穩定的X
有三個元件依賴X 就有三個理由不去更改它 我們稱X對這三個元件負有責任(responsible) 但因為X不依賴任何人 任何外部影響都不會影響到X 所以我們稱X是無依賴性的(independent)
下圖是個不穩定的Y
Y依賴於三個元件 我們稱Y是依賴性的(dependent)
穩定性metrics
Fan-In: 輸入依賴度 代表說多少外部元件依賴此元件內部的類別
Fan-Out: 輸出依賴度 代表多少此元件內部的類別依賴於別人
Instability: 不穩定性 I = (FO)/(FI+FO) 介於[0,1] 0代表非常穩定 1代表非常不穩定
上例子
算一下Component C 的不穩定性: FI = 3, FO = 1, I = 1/4
那在程式語言中怎麼算呢 在C++裡面就看#include的數量 在Java就看import的數量
看回SDP
SDP告訴我們 要朝著穩定的方向進行依賴 代表說
一個元件的I值應該大於他所倚賴的元件的I值 或者是說
I值必須按著被依賴的方向逐漸減少 就是越下層應該越穩定
再看個應用
SDP怎麼應用到實務上呢 假設今天你是負責元件Flexible
的工程師 這是個很容易被改動 你也預期會需要一直改動的元件
可是今天負責Stable
的團隊 使用了你寫的一個類別
好煩喔 因為別人依賴你 害你不能隨心所欲的改動 那你該怎麼辦呢? 沒錯 DIP
你仔細研究了一下 發現你的類別C被Stable
裡面的類別U使用了
於是我們建立一個US介面放進UServer
元件裡 這樣我就解除了Stable
對Flexible
的依賴 兩者都依賴於UServer
UServer
的I值是0 Flexible
還是維持著我想要的不穩定性(I=1) 而且所有依賴都順著I減小的方向依賴
只有一個介面的元件?
等等 你為了遵循SDP 多建了一個只包含介面的元件? 這個元件甚至不包含任何可以執行的程式碼 這合理嗎?
事實上 這很合理而且是個常見的必要策略 這些抽象的元件非常穩定(畢竟沒有任何實作) 因此是不太穩定的元件依賴的理想目標
穩定抽象原則 The Stable Abstraction Principle
A component should be as abstract as it is stable.
元件的抽象程度應該與元件的穩定程度一致
也就是說 一個穩定的元件也應該是抽象的 這代表說一個穩定的元件也要包含一些抽象類別 這樣就可對他進行擴展
來到了可愛的邏輯推理時間
SDP 告訴我們 要朝著穩定的方向進行依賴
SAP告訴我們穩定的元件就是抽象的元件
那SDP+SAP是什麼呢 答案是
依賴應該朝著抽象的方向進行
抽象性的metric
抽象性A怎麼計算呢 就是
(元件中抽象類別及介面的總數)/(元件中所有類別個數)
A介於[0,1] 0代表元件中沒有抽象類別 1代表元件中只有抽象類別
主角到齊了
我們定義了不穩定性I 以及抽象性A 我們可以建立一個以I為橫軸 A為縱軸的座標圖
你會發現最穩定的地方是座標圖的左上 最不穩定的地方是右下
注意 並不是所有元件都只落在這兩個地方 因為元件的抽象性跟依賴性有程度之分 比如你知道抽象類別也可能依賴於其他的抽象類別
但即便如此 還是有一些是元件不應該在的地方
分別是右上角的無用地帶 以及左下角的痛苦地帶
無用地帶
這個地方 代表元件有著最大的抽象 但卻沒有任何人依賴它 活像個廢材
痛苦地帶
在這裡的是高度穩定且具體的元件 我們不想要這種元件 因為他太具體所以很難進行擴展
而且要是這個元件很容易改變的話 那就加倍的痛苦了(因為很多人依賴它)
主序列
為了遠離無用地帶跟痛苦地帶 我們希望我們的元件可以分佈在(0,1)到(1,0)的線上 作者稱為主序列(Main Sequence)
既然我們知道越靠近主序列越好 那麼對於評價一個元件而言 我們就可以計算這個元件到主序列的距離
當我們量化了一個元件的好壞以後 我們就可以盡情地分析我們的系統啦
你可以用任何你會的統計知識 mean(平均數) variance(變異數) SD(標準差)等等 把一個系統內的所有元件的座標畫在座標圖上
當然也可以分析你不同的版本 來知道你的元件是不是越寫越偏了
總結
本篇文章介紹了元件與元件之間的關係該如何評分 你可以使用這些關於依賴性管理的metrics 來知道你的系統的依賴性如何