前端開發的耦合性與內聚性
以下將會以前端開發作為例子,讓大家更了解耦合與內聚的概念是什麼。
耦合(Coupling)
簡單來說就是相依性,如果討論的對象,彼此的相依性越強烈,那麼我們就可以說彼此的耦合性越大。
代表人物
如果要拿來擬人的話,最具代表性的人,大概就是國文課本的賣油翁了。
大家最熟悉的課文:
康肅問曰:「汝亦知射乎?吾射不亦精乎?」
翁曰:「無他,但手熟爾。」
康肅忿然曰:「爾安敢輕吾射!」
翁曰:「以我酌油知之。」
乃取一葫蘆置於地,以錢覆其口,徐以杓酌油瀝之,自錢孔入而錢不濕。
因曰:「我亦無他,惟手熟爾。」
賣油翁雖然其他的事並不會,但是他只負責賣油這個任務,而且很專精。
共用元件
耦合性可以讓同樣頁面擁有的Component, function…,重複被使用,因為我們在開發程式的時候,一定會時常遇到類似的情境需要用到類似的功能,因此會把這些東西便成為共用元件。
例如以下的例子:
- config.js
1
2
3
4
5export const categories = [
{label: '全部', value: 0},
{label: '未滿一年', value: 1},
{label: '一年', value: 2},
];
共用config可以讓全站皆可使用,但這也意味著被用越多次的話,耦合性就會越強大,也很容易發生改A壞B的狀況。
假設情境A - 共用config
今天有一個工程師收到一個需求,除了原本應在Client的config之外
要新增一個新的功能for business,文案一樣是“全部, 未滿一年, 一年…”
然而後端給予前端的資料型態為[全部: 1, 未滿一年: 2, 一年: 3…]
前端想當然不以為意,把原本的categoriesForClient改成:
1 | export const categories = [ |
最後發現,後端的Client和Business的schema並沒有對齊,一個value從0開始,一個從1開始,導致原本Client的顯示都不對,而位移了一格。
這種狀況就是高耦合性的config所造成的Side effects。
因此,前端應該要改成:
1 | export const categoriesForClient = [ |
相信以上面的例子就能充分了解耦合性帶來的好處與壞處了。
好處就是方便,加速開發時間的成本,提升耦合性也可以讓工程師在維護code更加容易。
壞處就是如果一個不小心就會發生改A壞B的狀況,很常發生在人與人溝通之間訊息不對等造成的誤會。
千萬要記住,要修改耦合性越高的元件,要越小心地去更動。
你可能會覺得設計看起來有點蠢?不,這種事情一定有機會碰到的,相信我!
假設情境B - UI元件
大部分的人認為,使用前端框架當然是要搭配UI component的design guideline開發效率才會提升呀
因此我們時常會在全站使用已經裝好的UI元件統一使用,就一般的情境來說,不太可能會碰到載了別人的套件還掛掉的狀況。
不過我們可能會拿下載好的UI component,然後override UI component變成自己的設計元件。
例如:ant-design, bootstrap……
內聚(Cohesion)
如果對象可以完成的事情越多,也代表這個對象所具備的能力或功能越強大,那麼就可以稱為內聚性越大。
代表人物
要說代表人物的話,應該就屬超人了,他可以做的事情很多,同時也代表他的責任重大,完成職責的時間量也很大。
假設情境
今天企劃開一個需求,要設計一個可以計算加法的計算機:
1 | const Caculator = (a, b) => { |
目前看起來好像很正常對吧?但是現在企劃說,想要再加減法功能:
1 | const Caculator = (a, b, sign) => { |
因應需要判斷加減法,所以多了一個參數判斷符號,裡面也多了判斷。
然而隨著需求的增加,程式也會變得越來越龐大:
1 | const Caculator = (a, b, sign) => { |
從這個計算機來看,你會發現他的耦合性是很低的,他只耦合了呼叫他的Component。
而這個Component本身可以計算加減乘除,非常的萬能,具備高內聚的特性。
但是你說這個component這樣設計好嗎?不就是低耦合高內聚的特性嗎?其實這見仁見智。
如果今天這個計算機Component他每一個計算scope內所包的程式很龐大,其實是不容易維護的。
講難聽一點,可能會變成好幾千行的一坨Component。
因此可以藉由提升耦合性讓程式更好維護。
藉由提升耦合性增加維護性
1 | const calulateHandler = (a, b, sign) => { |
我們將計算過程包裝成一個pure function,Caculator專注在頁面的UI顯示,而calulateHandler專注在計算,如此就能讓程式的分工變的明確,工程師在維護上也比較知道,如果是計算出問題就找calulateHandler,UI顯示要改變就找Caculator,可維護性大大的增加。
所以你也會發現,低耦合高內聚的程式其實也具備高擴展性的特性,你要調動功能是很容易的,他並不像高耦合性的程式不容易改動,容易出現Side effect的狀況,其實也因為他很容易擴充,所以大家低耦合高聚合才會成為大家時常聽到的一個設計目標。
違背單一職責原則
之前文章有討論到前端所用到的SOLID原則,而高內聚的程式正是違反了單一職責原則,應該要把職責一一切出來,而不應該把過多的成本放在同一個元件上。
哪個比較好
我們常常聽到很多人說開發程式要“低耦合, 高內聚”。
可是真的是這樣嗎?這可能要打一個很大的問號。
其實開發程式沒有絕對的,特別是在設計樣式套用的情境上需要更加小心。
有時候也要因應情境調整耦合和內聚各自佔的比例,過多或過少都會增加出狀況的風險。
千萬不可以為了設計樣式而設計程式,一旦用的不好,很容易讓團隊造成不小的衝擊。