我們來為內聚性舉一些例子,重點強調如何把其他的內聚性轉換成功能內聚性。當然,把各種各樣的子程序加以分類僅僅只是出於研究的用處,在實踐中花大力氣去區分每個子程序是那一種內聚類型是毫無意義的,下面的例子只是建議你在設計子程序的時候就要充分考慮如何把子程序寫得更好一些,也就是如何做到功能內聚。
內聚性舉例
以下是幾個內聚性的例子,其中既有好的,也有壞的:
功能內聚性例子。比如計算雇員年齡並給出生日的子程序就是功能內聚性的,因為它只完成一項工作,而且完成得很好。
順序內聚性的例子。假設有一個按給出的生日計算雇員年齡、退休時間的子程序,假如它是利用所計算的年齡來確定雇員將要退休的時間,那麼它就具有順序內聚性。而假如它是分別計算年齡和退休時間的,但使用相同生日數據,那它就只具有通訊內聚性。
確定程序存在哪種不良內聚性,還不如確定如何把它設計得更好重要。怎樣使這個子程序成為功能內聚性呢?可以分別建立兩個子程序,一個根據生日計算年齡,另外一個根據生日確定退休時間,確定退休時間子程序將調用計算年齡的程序,這樣,它們就都是功能內聚性的,而且,其它子程序也可以調用其中任一個子程序,或這兩個部調用。
通訊內聚性的例子。比如有一個打印總結報告,並在完成後重新初始化傳進來的總結數據的子程序,這個子程序具有通信內聚性,因為這兩個操作僅僅是由於它們使用了相同的數據才聯系在一起。
同前例一樣,我們考慮的重點還是如何把它變成是功能內聚性,總結數據應該在產生它的地方四周被重新初始化,而不應該在打印子程序中重新初始化。把這個子程序分為兩個獨立的子程序.第一個打印報告,第二個則在產生或者改動數據的代碼四周重新初始化數據。然後,利用一個較高層次的子程序來代替原來具有通訊相關的子程序,這個子程序將調用前面兩個分出來的子程序。
邏輯內聚性的例子。一個子程序將打印季度開支報告、月份開支報告和日開支報告.具體打印哪一個,將由傳入的控制標志決定,這個子程序具有邏輯內聚性,因為它的內部邏輯是由輸進去的外部控制標志決定的。顯然,這個子程序不是按只完成一項工作並把它作好的原則。怎樣使這個子程序變為功能內聚性呢?建立三個子程序:一個打印季度報告,一個打印月報告、一個打印日報告,改進原來的子程序,讓它根據傳送去控制標志來調用這三個子程序之一。調用子程序將只有調用代碼而沒有自己的計算代碼,因而具有功能內聚性。而三個被調用的手程序也顯然是功能內聚性的。非常巧合,這個只負責調用其它子程序的子程序也是一個事務處理中心。最好用如DispatchReporPrinting()之類帶有“調度”或“控制”等字眼的詞來給事務處理中心命名,以表示它只負責命令溫調度,而本身並不做任何工作。
邏輯內聚性的另一個例子。考慮一個負責打印開支報表、輸入新雇員名字並備份數據庫的子程序,其具體執行內容將由傳入的控制標志控制。這個子程序只具有邏輯內聚性,雖然這個關聯看起來是不合邏輯的。
要想使它成為功能內聚性,只要按功能把它分成幾部分就可以了。不過,這些操作有些過於凌亂。因此,最好重新建立一個調用各子程序的代碼。當擁有幾個需要調用的子程序時,重新組織調用代碼是比較輕易的。
過程內聚性的例子。假設有一個子程序,它產生讀取雇員的名字,然後是地址,最後是它的電話號碼。這種順序之所以重要,僅僅是因為它符合用戶的要求,用戶希望按這種順序進行屏幕輸入。另外一個子程序將讀取關於雇員的其它信息。這個子程序是過程內聚性,因為是由一個特定順序而不是其它任何原因,把這些操作組合在一起的。
與以前一樣,如何把它變為功能內聚性的答案仍然是把它分為幾個部分,並把這幾部分分別放入程序中。要保證調用子程序的功能是單一、完善的。調用子程序應該是諸如GetEmployeeData()這樣的子程序,而不該是像GetFirstPartofEmployeeData()這類的子程序。可能還要改動其余讀取數據的子程序。為得到功能內聚性,改動幾個子程序是很正常的。
同時具有功能和臨時內聚性的程序。考慮一個具有完成一項事物處理所有過程的子程序,從用戶那裡讀取確認信息,向數據存入一個記錄,清除數據域,並給計數器加1。這個程序是功能內聚性的,因為它只從事一項工作,進行事物處理,但是,更確切地說,這個子程序同時也是臨時內聚性的,不過當一個子程序具有兩種以上內聚性時,一般只考慮最強的內聚性。
這個例子提出了如何用一個名字恰如其分地抽象描述出程序內容的問題。比如可以稱這個子程序為ConfirmEntryAndAdjustData(),表示這個干程序僅具有偶然內聚性。而假如稱它為CompleteTransaction(),那麼就可能清楚地表示出這個子程序僅具有一個功能,而已具有功能內聚性。
過程性、臨時或者可能的邏輯內聚性。比如一個進行某種復雜計算前5個操作,並把中間結果返回到調用子程序。由於5 項操作可能要用好幾個小時,因此當系統癱瘓時,這個子程序將把中間結果存入一個文件中,然後,這個子程序檢查磁盤,以確定其是否有足夠空間來存儲最後計算結果,並把磁盤狀態和中間結果返回到調用程序。
這個子程序很可能是過程內聚性的,但你也可能認為它具有臨時內聚性,甚至具有邏輯內聚性。不過,不要忘了問題的要害不是爭論它具有哪種不好的內聚性,而是如何改善其內聚性。原來的子程序是由一系列令人莫名其妙的操作組成的,與功能內聚性相距甚遠,首先,調用子程序不應該調用一個,而應該調用幾個獨立的子程序:l)進行前5步計算的子程序;2)把中間結果存入一個文件;3)確定可用的磁盤存儲空間。假如調用子程序被稱作ComputeExtravagantNumber(),那麼它不應該把中間結果寫入一個文件,也決不該為後來的操作檢查磁盤剩余空間,它所作的就僅限於計算一些數而已。對這個子程序的精心重新設計,將至少影響到一至兩個層次上的子程序,對於這頂任務的較好設計。
圖中畫陰影的部分是由原來的子程序從事的工作,在新組織結構中它們位於不同的層次上,這就是為什麼為了把這些工作放人恰當的子程序中,要進行這麼多重新組織工作的原因。用幾個功能內聚性的子程序來代替一個具有不良內聚性的子程序是很平常的。
在實踐中,我們會碰到各種各樣內舉類型的子程序,除了功能內聚型以外,任何類型的子程序都可以通過增加層次的方式來把它分為幾個功能內聚型的子程序,例如上面邏輯內聚型的例子,在把功能分給幾個功能子程序後,在功能之上增加了一個控制的程序。這是典型的非面向對象語言(如C語言)的做法,而在面向對象語言中(如C++、Java),可以通過重載、接口等技術來實現。