連載:面向物件葵花寶典:思想、技巧與實踐(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類是很不穩定的。
所以,無論是“低內聚”,還是“高耦合”,其本質都是“不穩定”,不穩定就會帶來工作量,帶來風險,這當然不是我們希望看到的,所以我們應該做到“高內聚低耦合”。
回答完第一個問題後,我們來看第二個問題:高內聚低耦合是否意味著內聚越高越好,耦合越低越好?
按照我們前面的解釋,內聚越高,一個類越穩定;耦合越低,一個類也很穩定,所以當然是內聚越高越好,耦合越低越好了。
但其實稍有經驗的同學都會知道這個結論是錯誤的,並不是內聚越高越好,耦合越低越好,真正好的設計是在高內聚和低耦合間進行平衡,也就是說高內聚和低耦合是衝突的。
我們詳細來分析一下為什麼高內聚和低耦合是衝突的。
對於內聚來說,最強的內聚莫過於一個類只寫一個函式,這樣內聚性絕對是最高的。但這會帶來一個明顯的問題:類的數量急劇增多,這樣就導致了其它類的耦合特別多,於是整個設計就變成了“高內聚高耦合”了。由於高耦合,整個系統變動同樣非常頻繁。
同理,對於耦合來說,最弱的耦合是一個類將所有的函式都包含了,這樣類完全不依賴其它類,耦合性是最低的。但這樣會帶來一個明顯的問題:內聚性很低,於是整個設計就變成了“低耦合低內聚”了。由於低內聚,整個類的變動同樣非常頻繁。
對於“低耦合低內聚”來說,還有另外一個明顯的問題:幾乎無法被其它類重用。原因很簡單,類本身太龐大了,要麼實現很複雜,要麼資料很大,其它類無法明確該如何重用這個類。
所以,內聚和耦合的兩個屬性,排列組合一下,只有“高內聚低耦合”才是最優的設計。
因此,在實踐中我們需要牢牢記住需要在高內聚和低耦合間進行平衡,而不能走極端。 具體如何平衡,且聽下回分解。
================================================
轉載請註明出處:http://blog.csdn.net/yunhua_lee/article/details/25074707
================================================