連載:麵向對象葵花寶典:思想、技巧與實踐(29) - 高內聚低耦合
高內聚低耦合,可以說是每個程序猿,甚至是編過程序,或者僅僅隻是在大學裏麵學過計算機,都知道的一個簡單的設計原則。
雖然如此流行和人所眾知,但其實真正理解的人並不多,很多時候都是人雲亦雲。
===============================================================
要想真正理解“高內聚低耦合”,需要回答兩個問題:
1)為什麼要高內聚低耦合?
2)高內聚低耦合是否意味內聚越高越好,耦合越低越好?
第一個問題:為什麼要高內聚低耦合?
經典的回答是:降低複雜性。
確實很經典,當然,其實也是廢話!我相信大部分人看了後還是不懂,什麼叫複雜性呢?
要回答這個問題,其實可以采用逆向思維,即:如果我們不做到這點,將會怎樣?
首先來看內聚,試想一下,假如我們是低內聚,情況將會如何?
前麵我們在闡述內聚的時候提到內聚的關鍵在於“元素的凝聚力”,如果內聚性低,則說明凝聚力低;對於一個團隊來說,如果凝聚力低,則一個明顯的問題是“不穩定”;對於一個模塊來說,內聚性低的問題也是一樣的“不穩定”。具體來說就是如果一個模塊內聚性較低,則這個模塊很容易變化。一旦變化,設計、編碼、測試、編譯、部署的工作量就上來了,而一旦一個模塊變化,與之相關的模塊都需要跟著改變。
舉一個簡單的例子,假設有這樣一個設計不好的類:Person,其同時具有“學生”、“運動員”、“演員”3個職責,有另外3個類“老師”、“教練”、“導演”依賴這個類。
Person.java
package com.oo.cohesion.low; /** * “人”的類設計 * */ public class Person { /** * 學生的職責:學習 */ public void study() { //TODO: student's responsibility } /** * 運動員的職責:運動 */ public void play(){ //TODO: sportsman's responsibility } /** * 演員的職責:扮演 */ public void act(){ //TODO: actor's responsibity } }
Teacher.java
package com.oo.cohesion.low; /** * “老師”的類設計 * */ public class Teacher { public void teach(Person student){ student.study(); //依賴Person類的“學生”相關的職責 } }
Coach.java
package com.oo.cohesion.low; /** * “教練”的類設計 * */ public class Coach { public void train(Person trainee){ trainee.play(); //依賴Person類的“運動員”職責 } }
Director.java
package com.oo.cohesion.low; /** * “導演”的類設計 * */ public class Director { public void direct(Person actor){ actor.act(); //依賴Person類“演員”的相關職責 } }
在上麵的樣例中,Person類就是一個典型的“低內聚”的類,很容易發生改變。比如說,現在老師要求學生也要考試,則Person類需要新增一個方法:test,如下:
package com.oo.cohesion.low; /** * “人”的類設計 * */ public class Person { /** * 學生的職責:學習 */ public void study() { //TODO: student's responsibility } /** * 學生的職責:考試 */ public void test(){ //TODO: student's responsibility } /** * 運動員的職責:運動 */ public void play(){ //TODO: sportsman's responsibility } /** * 演員的職責:扮演 */ public void act(){ //TODO: actor's responsibity } }
由於Coach和Director類都依賴於Person類,Person類改變後,雖然這個改動和Coach、Director都沒有關係,但Coach和Director類都需要重新編譯測試部署(即使是PHP這樣的腳本語言,至少也要測試)。
同樣,Coach和Director也都可能增加其它對Person的要求,這樣Person類就需要同時兼顧3個類的業務要求,且任何一個變化,Teacher、Coach、Director都需要重新編譯測試部署。
對於耦合,我們采用同樣的方式進行分析,即:如果高耦合,將會怎樣?
高耦合的情況下,模塊依賴了大量的其它模塊,這樣任何一個其它依賴的模塊變化,模塊本身都需要受到影響。所以,高耦合的問題其實也是“不穩定”,當然,這個不穩定和低內聚不完全一樣。對於高耦合的模塊,可能本身並不需要修改,但每次其它模塊修改,當前模塊都要編譯、測試、部署,工作量同樣不小。
我們同樣以一個樣例來說明。假設我們要設計一個Boss類,Boss是要管整個公司的,那麼我們假設這是一家“麻雀雖小五髒俱全”的公司,同時有“研發、測試、技術支持、銷售、會計、行政”等部門。
Boss.java
package com.oo.coupling.high; /** * “老板”類 * */ public class Boss { private Tester tester; private Developer developer; private Supporter supporter; private Administration admin; private Accountant accountant; /** * Boss每天檢查工作,看到下麵的代碼,是否覺得Boss很忙? */ public void check(){ //檢查測試工作 tester.report(); //檢查研發工作 developer.report(); //檢查技術支持工作 supporter.report(); //檢查行政工作 admin.report(); //檢查財務工作 accountant.report(); } }
Accountant.java
package com.oo.coupling.high; /** * “財務”類 * */ public class Accountant { public void report(){ System.out.print("Accountant report"); } }
Administration.java
package com.oo.coupling.high; /** * “行政”類 * */ public class Administration { public void report(){ System.out.print("Administration report"); } }
Developer.java
package com.oo.coupling.high; /** * “開發”類 * @author Administrator * */ public class Developer { public void report(){ System.out.print("Developer report"); } }
Supporter.java
package com.oo.coupling.high; /** * “技術支持”類 * */ public class Supporter { public void report(){ System.out.print("Supporter report"); } }
Tester.java
package com.oo.coupling.high; /** * “測試”類 * */ public class Tester { public void report(){ System.out.print("Tester report"); } }
好吧,Boss很忙,我們也很欽佩,但是有一天,研發的同學覺得他們應該做更多的事情,於是他們增加了一個“研究”的工作,且這個工作也不需要報告給Boss,例如:
package com.oo.coupling.high; /** * “開發”類 * @author Administrator * */ public class Developer { public void report(){ System.out.print("Developer report"); } /** * 研發新增加一個研究的任務,這個任務也不需要向Boss匯報 */ public void research(){ System.out.print("Developer is researching big data, hadoop :)"); } }
雖然這個工作不需要報告給Boss,但由於Developer類修改了,那麼Boss類就需要重新編譯測試部署。其它幾個Tester、Supporter類等也是類似,一旦改變,即使這些類和Boss類半毛錢關係都沒有,Boss類還是需要重新編譯測試部署,所以Boss類是很不穩定的。
所以,無論是“低內聚”,還是“高耦合”,其本質都是“不穩定”,不穩定就會帶來工作量,帶來風險,這當然不是我們希望看到的,所以我們應該做到“高內聚低耦合”。
回答完第一個問題後,我們來看第二個問題:高內聚低耦合是否意味著內聚越高越好,耦合越低越好?
按照我們前麵的解釋,內聚越高,一個類越穩定;耦合越低,一個類也很穩定,所以當然是內聚越高越好,耦合越低越好了。
但其實稍有經驗的同學都會知道這個結論是錯誤的,並不是內聚越高越好,耦合越低越好,真正好的設計是在高內聚和低耦合間進行平衡,也就是說高內聚和低耦合是衝突的。
我們詳細來分析一下為什麼高內聚和低耦合是衝突的。
對於內聚來說,最強的內聚莫過於一個類隻寫一個函數,這樣內聚性絕對是最高的。但這會帶來一個明顯的問題:類的數量急劇增多,這樣就導致了其它類的耦合特別多,於是整個設計就變成了“高內聚高耦合”了。由於高耦合,整個係統變動同樣非常頻繁。
同理,對於耦合來說,最弱的耦合是一個類將所有的函數都包含了,這樣類完全不依賴其它類,耦合性是最低的。但這樣會帶來一個明顯的問題:內聚性很低,於是整個設計就變成了“低耦合低內聚”了。由於低內聚,整個類的變動同樣非常頻繁。
對於“低耦合低內聚”來說,還有另外一個明顯的問題:幾乎無法被其它類重用。原因很簡單,類本身太龐大了,要麼實現很複雜,要麼數據很大,其它類無法明確該如何重用這個類。
所以,內聚和耦合的兩個屬性,排列組合一下,隻有“高內聚低耦合”才是最優的設計。
因此,在實踐中我們需要牢牢記住需要在高內聚和低耦合間進行平衡,而不能走極端。 具體如何平衡,且聽下回分解。
================================================
轉載請注明出處:https://blog.csdn.net/yunhua_lee/article/details/25074707
================================================
最後更新:2017-04-03 12:56:33