設計原理
模塊化
模塊是由邊界元素限定的相鄰程序元素的序列,而且有一個總體標識符代表它。
按照模塊的定義,過程、函數、子程序和宏,都可作為模塊。
面向對象方法學中的對象是模塊,對象內的方法也是模塊。模塊是構成程序的基本構件。
模塊化就是把程序劃分成獨立命名且可獨立訪問的模塊,每個模塊完成一個子功能,把這些模塊集成起來構成一個整體,可以完成指定的功能滿足用戶的需求
當模塊數目增加時每個模塊的規模將減小,開發單個模塊需要的成本確實減少了,但是,隨著模塊數目的增加,設計模塊間接口所需要的工作量也將增加,根據這兩個因素,得出圖中的總成本曲線。
每個程序都相應地有一個最適當的模塊數目M,使得系統的開發成本最小,雖然目前還不能精確地決定M的數值,但是在考慮模塊化的時候總成本曲線確實是有也的指南。
采用模塊化原理可以使軟件結構清晰,不僅容易設計也容易閱讀和理解。
抽象
人類在認識復雜現象的過程中使用的最強有力的思維工具是抽象。人們在實踐中認識到,在現實世界中一定事物、狀態或過程之間總存在著某些相似的方面(共性)。把這些相似的方面集中和概括起來,暫時忽略它們之間的差異,這就是抽象。或者說抽象就是抽出事物的本質特性而暫時不考慮它們的細節。
由于人類思維能力的限制,如果每次面臨的因素太多,是不可能產生精確思維的。處理復雜系統的唯有效的方法是用層次的方式構造和分析它。一個復雜的動態系統首先可以用一些高級的抽象概念構造和理解.這些高級概念又可以用一些較低級的概念構造和理解,如此進行下去,直至最低層次的具體元素。
這種層次的思維和解題方式必須反映在定義動態系統的程序結構之中,每級的一個概念將以某種方式對應于程序的一組成分。當考慮對任何問題的模塊化解法時,可以提出許多抽象的層次。在抽象的最高層次使用問題環境的語言,以概括的方式敘述問題的解法;在較低抽象層次采用更過程化的方法,把面向問題的術語和面向實現的術語結合起來敘述問題的解法;最后,在最低的抽象層次用可以直接實現的方式敘述問題的解法。
軟件工程過程的每一步都是對軟件解法的抽象層次的一次精化。在可行性研究階段,軟件作為系統的一個完整部件:在需求分析期間,軟件解法是使用在問題環境內熟悉的方式描述的:當由總體設計向詳細設計過渡時,抽象的程度也就隨之減少了;最后,當源程序寫出來以后,也就達到了抽象的最低層。
逐步求精和模塊化的概念,與抽象是緊密相關的。隨著軟件開發工程的進展,在軟件結構每層中的模塊,表示了對軟件抽象層次的一次精化。事實上,軟件結構頂層的模塊,控制了系統的主要功能并且影響全局:在軟件結構底層的模塊,完成對數據的一個具體處理。用自頂向下由抽象到具體的方式分配控制,簡化了軟件的設計和實現,提高了軟件的可理解性和可測試性,并且使軟件更容易維護。
逐步求精
逐步求精是人類解決復雜問題時采用的基本方法,也是許多軟件工程技術(例如,規格說明技術,設計和實現技術)的基礎。可以把逐步求精定義為:“為了能集中精力解決主要問題而盡量推遲對問題細節的考慮。”
逐步求精之所以如此重要,是因為人類的認知過程遵守Miller 法則:一個人在任何時候都只能把注意力集中在(7土2)個知識塊上。
但是,在開發軟件的過程中,軟件工程師在一段時間內需 要考慮的知識塊數遠遠多于7。例如,一個程序通常不止使用7個數據,一個用戶也往往有不止7個方面的需求。逐步求精方法的強大作用就在于,它能幫助軟件工程師把精力集中在與當前開發階段最相關的那些方面上,而忽略那些對整體解決方案來說雖然是必要的,然而目前還不需要考慮的細節.這些細節將留到以后再考慮。Miller 法則是人類智力的基本局限,人們不可能戰勝自己的自然本性,只能接受這個事實,承認自身的局限性,并在這個前提下盡自己的最大努力工作。
事實上,可以把逐步求精看作是項把一個時期內必須解決的種種問題按優先級排序的技術。逐步求精方法確保每個問題都將被解決,而且每個問題都將在適當的時候被解決,但是,在任何時候一個人都不需要同時處理7個以上知識塊。逐步求精最初是由Niklaus Wirth提出的種自頂向下的設計策略。按照這種設計策略,程序的體系結構是通過逐步精化處理過程的層次而設計出來的。通過逐步分解對功能的宏觀陳述而開發出層次結構,直至最終得出用程序設計語盲表達的程序。
Wirth本人對逐步求精策略曾做過如下的概括說明。“我們對付復雜問題的最重要的辦法是抽象,因此,對一個復雜的問題不應該立刻用計算機指令、數字和邏輯符號來表示,而應該用較自然的抽象語句來表示,從而得出抽象程序。抽象程序對抽象的數據進行某些特定的運算并用某些合適的記號(可能是自然語言)來表示。對抽象程序做進一步的分解,并進入下 一個抽象層次,這樣的精細化過程一直進行下去,直到程序能被計算機接受為止。這時的程序可能是用某種高級語言或機器指令書寫的。”
求精實際上是細化過程。
人們從在高抽象級別定義的功能陳述(或信息措述)開始,也就是說,該陳述僅僅概念性地描述了功能或信息,但是并沒有提供功能的內部工作情況或信息的內部結構。求精要求設計者細化原始陳述,隨著每個后續求精(即細化)步驟的完成而提供越來越多的細節。
抽象與求精是對互補的概念。
抽象使得設計者能夠說明過程和數據,同時卻忽略了低層細節。事實上,可以把抽象看作是一種通過忽略多余的細節同時強調有關的細節,而實現逐步求精的方法。求精則幫助設計者在設計過程中逐步揭示出低層細節。這兩個概念都有助于設計者在設計演化過程中創造出完整的設計模型。
信息隱藏和局部化
應用模塊化原理時,自 然會產生的一個問題是:“為了得到最好的-組模塊,應該怎樣分解軟件呢?”信息隱藏原理指出:應該這樣設計和確定模塊,使得一個模塊內包含的信息(過程和數據)對于不需要這些信息的模塊來說,是不能訪問的。局部化的概念和信息隱藏概念是密切相關的。所謂局部化是指把些 關系密切的軟件元素物理地放得彼此靠近。在模塊中使用局部數據元素是局部化的一個例子。顯然。局部化有助于實現信息隱藏。
實際上,應該隱藏的不是有關模塊的切信息,而是 模塊的實現細節。 因此,有人主張把這條原理稱為“細節胞藏”。在“隱藏”意味著有效的模塊化可以通過定又 一組獨立的模塊面實現,這些獨立的模塊彼此間僅僅交換那些為了完成系統功能而必須交換的信息。如果在測試期間和以后的軟件維護期間需要修改軟件,那么使用信息隱藏原理作為模塊化系統設計的標準就會帶來極大好處。因為絕大多數數據和過程對于軟件的其他部分面言是隱藏的(也就是“看”不見的),在修改期間由于疏忽面引人的錯誤就很少可能傳播到軟件的其他部分。
模塊獨立
模塊獨立的概念是模塊化抽象、信息隱藏和局部化概念的直接結果。
開發具有獨立功能面且和其他模塊之間沒有過多的相互作用的模塊,就可以做到模塊獨立。換句話說,希望這樣設計軟件結構,使得每個模塊完成一個 相對獨立的特定子功能,并且和其他模塊之間的關系很簡單。
為什么模塊的獨立性很重要呢?主要有兩條理由:
第一,有效的模塊化(即具有獨立的模塊)的軟件比較容易開發出來。這是由于能夠分割功能而且接口可以簡化.當許多人分工合作開發同一個軟件時,這個優點尤其重要。
第二,獨立的模塊比較容易測試和維護。這是因為相對說來,修改設計和程序需要的工作量比較小,錯誤傳播范圍小,需要擴充功能時能夠“插入”模塊。總之,模塊獨立是好設計的關鍵,而設計又是決定軟件質量的關鍵環節。
模塊的獨立程度可以由兩個定性標準度量,這兩個標準分別稱為內聚和耦合。耦合衡量不同模塊彼此間互相依賴(連接)的緊密程度:內聚衡量一個模塊內部各個元素彼此結合的緊密程度。以下分別詳細闡述。
1.耦合
耦合是對一個軟件結構內不同模塊之間互連程度的度量。桐合強弱取決于模塊間接口的復雜程度,進人或訪問一個模塊的點,以及通過接口的數據。
在軟件設計中應該追求盡可能松散耦合的系統。在這樣的系統中可以研究、測試或維護任何一個模塊,而不需要對系統的其他模塊有很多了解。此外,由于模塊間聯系簡單,發生在一處的錯誤傳播到整個系統的可能性就很小。因此,模塊間的耦合程度強烈影響著系統的可理解性、可測試性、可靠性和可維護性。
怎樣具體區分模塊間耦合程度的強弱呢?
如果兩個模塊中的每一個都能獨立地工作而不需要另一個模塊的存在,那么它們彼此完全獨立.這意味著模塊間無任何連接,概合程度最低。但是,在一個軟件系統中不可能所有模塊之間都沒有任何連接。
如果兩個模塊彼此間通過參數交換信息,而且交換的信息僅僅是數據,那么這種耦合稱為數據耦合。如果傳遞的信息中有控制信息(盡管有時這種控制信息以數據的形式出現),則這種耦合稱為控制耦合。
數據耦合是低耦合。系統中至少必須存在這種耦合,因為只有當某些模塊的輸出數據作為另一些模塊的輸人數據時,系統才能完成有價值的功能。一般說來,一個系統內可以只包含數據耦合。控制耦合是中等度的耦合,它增加了系統的復雜程度。控制耦合往往是多余的,在把模塊適當分解之后通常可以用數據耦合代替它。
如果被調用的模塊需要使用作為參數傳遞進來的數據結構中的所有元素,那么,把整個數據結構作為參數傳遞就是完全正確的。但是,當把整個數據結構作為參數傳遞而被調用的模塊只需要使用其中一部分數據元素時,就出現了特征耦合。在這種情況下,被調用的模塊可以使用的數據多于它確實需要的數據,這將導致對數據的訪問失去控制,從而給計算機犯罪提供了機會。
當兩個或多個模塊通過一個公 共數據環境相互作用時,它們之間的耦合稱為公共環境耦合。公共環境可以是全程變量、共享的通信區、內存的公共覆蓋區、任何存儲介質上的文件、物理設備等。
公共環境耦合的復雜程度隨耦合的模塊個數而變化,當耦合的模塊個數增加時復雜程度顯著增加。如果只有兩個模塊有公共環境,那么這種耦合有下面兩種可能。
(1)一個模塊往公共環境送數據,另一個模塊從公共環境取數據。這是數據耦合的一種形式,是比較松散的耦合。
(2)兩個模塊都既往公共環境送數據又從里面取數據,這種耦合比較緊密,介于數據耦合和控制耦合之間。如果兩個模塊共享的數據很多,都通過參數傳遞可能很不方便,這時可以利用公共環境耦合。
最高程度的耦合是內容耦合。如果出現下列情況之一, 兩個模塊間就發生了內容耦合
1、一個模塊訪問另一個模塊的內部數據。
2、一個模塊不通過正常人口而轉到另一個模塊的內部。
3、兩個模塊有一部分程序代碼重疊(只可能出現在匯編程序中)。
4、一個模塊有多個人口(這意味著一個模塊有幾種功能)。
應該堅決避免使用內容耦合。事實上許多高級程序設計語言已經設計成不允許在程序中出現任何形式的內容耦合。
總之,耦合是影響軟件復雜程度的一個重要因素。應該采取下述設計原則:盡量使用數據耦合,少用控制耦合和特征耦合,限制公共環境耦合的范圍,完全不用內容耦合。
2.內聚
內聚標志著一個模塊內各個元素彼此結合的緊密程度,它是信息隱藏和局部化概念的自然擴展。簡單地說,理想內聚的模塊只做一件事情 。設計時應該力求做到高內聚,通常中等程度的內聚也是可以采用的,而且效果和高內聚相差不多。內聚和耦合是密切相關的,模塊內的高內聚往往意味著模塊間的松耦合。內聚和耦合都是進行模塊化設計的有力工具,但是實踐表明內聚更重要,應該把更鄉注意力集中到提高模塊的內聚程度上。
低內聚有如下兒類:如果-個模塊完成一組任務,這些任務彼此間即使有關系,關系也是很松散的,就叫做偶然內聚。有時在寫完一個程序之后,發現 組語句在兩處成多處出現,于是把這些語句作為個模塊以節 省內存.這樣就出現了偶然內聚的模塊。如果一個模塊完成的任務在邏輯上屬于相同或相似的一類(例如 個模塊產生 各種類型的全部輸出)則稱為邏輯內聚。如果一個模塊包含的任務必須在同一段時間內執行(例如,模塊完成各種初始化工作),就叫時間內聚。
在偶然內聚的模塊中,各種元素之間沒有實質性聯系,很可能在一種應用場合需要 修改這個模塊,而在另種應用場合又不允許這種修改,從而陷人困境。 事實上,偶然內聚
的模塊出現修改錯誤的概率比其他類型的模塊高得多。
在邏輯內聚的模塊中,不同功能混在一起,合用部分程序代碼即使局部功能的修改有時也會影響全局。因此,這類模塊的修改也比較困難。
時間關系在一定程度上反映了程序的某些實質,所以時間內聚比邏輯內聚好一些。中內聚主要有兩類:如果一個模塊內的處理元素是相關的,而且必須以特定次序執行,則稱為過程內聚。使用程序流程圖作為工具設計軟件時,常常通過研究流程圖確定模塊的劃分,這樣得到的往往是過程內聚的模塊。如果模塊中所有元素都使用同一個輸人數據和(或)產生同一個輸出數據,則稱為通信內聚。
高內聚也有兩類:如果一個模塊內的處理元素和同一個功能密切相關,而且這此處理必須順序執行(通常一個處理元素的輸出數據作為下一個處理元素的輸人數據),則稱為順序內聚。根據數據流圖劃分模塊時,通常得到順序內聚的模塊,這種模塊彼此間的連接往往比較簡單。如果模塊內所有處理元素屬于一個整體,完成一個單一的功能,則稱為功能內聚。功能內聚是最高程度的內聚。
耦合和內聚的概念是Constantine, Yourdon,Myers和Stevens等人提出來的。按照他們的觀點,如果給上述7種內聚的優劣評分,將得到如下結果:
功能內聚10 分??? 時間內聚3 分
順序內聚9 分????? 邏輯內聚1 分
通信內聚7 分????? 偶然內聚0 分
過程內聚5 分
事實上,沒有必要精確確定內聚的級別。重要的是設計時力爭做到高內聚,并且能夠辨認出低內聚的模塊,有能力通過修改設計提高模塊的內聚程度并且降低模塊間的耦合程度,從而獲得較高的模塊獨立性。