1. 程式人生 > >連載:面向物件葵花寶典:思想、技巧與實踐(29)

連載:面向物件葵花寶典:思想、技巧與實踐(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這樣的指令碼語言,至少也要測試)。

同樣,CoachDirector也都可能增加其它對Person的要求,這樣Person類就需要同時兼顧3個類的業務要求,且任何一個變化,TeacherCoachDirector都需要重新編譯測試部署。

對於耦合,我們採用同樣的方式進行分析,即:如果高耦合,將會怎樣?

高耦合的情況下,模組依賴了大量的其它模組,這樣任何一個其它依賴的模組變化,模組本身都需要受到影響。所以,高耦合的問題其實也是“不穩定”,當然,這個不穩定和低內聚不完全一樣。對於高耦合的模組,可能本身並不需要修改,但每次其它模組修改,當前模組都要編譯、測試、部署,工作量同樣不小。

我們同樣以一個樣例來說明。假設我們要設計一個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
================================================