天天看點

《代碼大全》學習筆記(5):高品質子程式特點

        什麼叫“子程式”?子程式是具有單一功能的可調用的函數或過程。

5.1 生成子程式的原因

        降低複雜性:使用子程式的最首要原因是為了降低程式的複雜性,可以使用子程式來隐含資訊,進而使你不必再考慮這些資訊;一個子程式需要從另一個子程式中脫離出來的原因之一是,過多重數的内部循環和條件判斷。

        限制了改動帶來的影響:由于在獨立區域進行改動,是以,由此帶來的影響也隻限于一個或最多幾個區域中。

        隐含順序:把處理事件的非特定順序隐含起來。

        改進性能:通過使用子程式,可以隻在一個地方,而不是同時幾個地方優化代碼段。

        進行集中控制。

        隐含資料結構:可以把資料結構的實作細節隐含起來,這樣,絕大部分程式都不必擔心這種雜亂的計算機科學結構,而可以從問題域中資料是如何使用的角度來處理資料。

        隐含全局變量。

        隐含指針操作:指針操作可讀性很差,而且很容易引發錯誤;通過把它們獨立在子程式中,可以把注意力集中到操作意圖而不是機械的指針操作本身。

        重新使用代碼段:放進子產品化子程式中的代碼段重新使用。

        計劃開發一個程式族:如果想改進一個程式,最好把将要改動的那部分放進子程式中,将其獨立。

        提高部分代碼的可讀性:把一段代碼放入一個精心命名的子程式,是說明其功能的最好辦法。

        提高可移植性:可以使用子程式來把不可移植部分、明确性分析和将來的移植性工作分隔開來,不可移植的部分包括:非标準語言特性、硬體的依賴性和作業系統的依賴性等。

        分隔複雜操作:複雜操作包括繁雜的算法、通信協定、棘手的布爾測試、對複雜資料的操作等等。

        獨立非标準語言函數的使用:絕大多數實作語言都含有一些非标準的但卻友善的擴充。

        簡化複雜的布爾測試:很少有必要為了解程式流程而去了解複雜的布爾測試。

5.1.1 簡單而沒有寫入子程式的操作

        小型子程式有許多優點,其中之一是改進了可讀性。

5.1.2 建立子程式的理由總結

        建立子程式的理由概述:降低複雜性、避免重複代碼段、限制改動帶來的影響、隐含順序、改進性能、進行集中控制、隐含資料結構、隐含指針操作、隐含全局變量、促進重新使用代碼段、計劃開發一個軟體族、改善某一代碼段可讀性、改善可移植性、分隔複雜操作、獨立非标準語言函數的使用、簡化複雜的布爾測試。

5.2 子程式名稱恰當

        子程式有效命名的指導方針:1)對于過程的名字,可以用一個較強的動詞帶目标的形式;2)對于函數名字,可以使用傳回值的描述;3)避免無意義或者模棱兩可的動詞;4)描述子程式所做的一切;5)名字的長度要符合需要;6)建立用于通用操作的約定。

5.3 強内聚性

        内聚性指的是在一個子程式中,各種操作之間互相聯系的緊密程度;有些程式員喜歡用“強度”一詞來代替内聚性。

5.3.1 可取的内聚性

        通常認為是可以接受的一些内聚類型:1)功能内聚性(當程式執行一項并且僅僅是一項工作時,就是這種内聚性);2)順序内聚性(是指在子程式内包含需要按特定順序進行的、逐漸分享資料而又不形成一個完整功能的操作);3)通訊内聚性(通訊内聚性是在一個子程式中,兩個操作隻是使用相同資料,而不存在其它任何聯系時産生的);4)臨時内聚性(因為同時執行的原因才被放入同一個子程式裡,這時産生臨時内聚性)。

5.3.2 不可取的内聚性

        過程内聚性:當子程式中的操作是按某一特定順序進行的,就是過程内聚性;與順序内聚性不同,過程内聚性中的順序操作使用的并不是相同資料。

        邏輯内聚性:當一個子程式中同時含有幾個操作,而其中一個操作又被傳進來的控制标志所選擇時,就産生了邏輯内聚性;之是以稱之為邏輯内聚性,是因為這些操作僅僅是因為控制流,或者說“邏輯”的原因才聯系到一起的。

偶然内聚性:當同一個子程式中的操作之間無任何聯系時,為偶然内聚性,也叫做“無内聚性”。

5.3.3 内聚性舉例

5.4 松散耦合性

        所謂耦合性指的是兩個子程式之間聯系的緊密程度;耦合性與内聚性是不同的,内聚性是指一個子程式的内部各部分之間的聯系程度,而耦合指的是子程式之間的聯系程度。

        子程式之間具有良好耦合的特點是它們之間的耦合是非常松散的,任一個子程式都能很容易地被其它子程式調用;在建立一個子程式時,應盡量避免它對其它子程式有依賴性。

5.4.1 耦合标準

        幾條估計子程式間耦合程度的标準:

        耦合規模:所謂耦合規模是指兩個子程式之間聯系的數量多少;對于耦合來說,聯系的數量越少越好。

        密切性:密切性指的是兩個子程式之間聯系的直接程度;聯系越直接越好,兩個子程式之間聯系最密切的是參數表中的參數。

        可見性:是指兩個子程式之間聯系的顯著程度。

        靈活性:是指改變兩個子程式之間聯系的容易程度。

5.4.2 耦合層次

        簡單資料耦合:如果兩個子程式之間傳遞的資料是非結構化的,并且全部都是通過參數表進行的,這通常稱作“正常耦合”,這也是一種最好的耦合。

        資料結構耦合:如果在兩個程式之間傳遞的資料是結構化的,并且是通過參數表實作傳遞的,它們之間就是資料結構耦合的;這種耦合有時也稱之為“郵票耦合”(stamp coupling)。

        控制耦合:如果一個子程式通過傳入另一個子程式的資料通知它該作什麼,那麼這兩個子程式就是控制耦合的。

全局資料耦合:如果兩個子程式使用同一個全局資料,那它就是全局資料耦合的;這也就是通常所說的“公共耦合”或“全局耦合”。

        不合理耦合(pathological):如果一個子程式使用了另外一個子程式中代碼,或者它改變了其中的局部變量,那麼它們就是不合理耦合的;這種耦合也稱之為“内容耦合”。

5.4.3 耦合舉例

5.5 長度

       理論上,常把一個子程式的最佳長度定為一兩頁,即66到132行。

5.6 防錯性程式設計

        在防錯性程式設計中,其中心思想是,即使一個子程式被傳入了壞資料,它也不會被傷害,哪怕這個資料是由其它子程式錯誤而産生的。

5.6.1 使用斷言

        斷言是一個在假設不正确時會大聲抗議的函數或宏指令,可以使用斷言來驗證在程式中做出的假設并排除意外情況。

下面是使用斷言的一些指導方針:1)如果有預處理程式的話,使用預處理程式宏指令;2)在斷言中應避免使用可執行代碼,把可執行代碼放入斷言。

5.6.2 輸入垃圾不一定輸出垃圾

      “輸入垃圾,輸出垃圾”,往往是劣質程式。

       檢查所有外部程式輸入的數值;檢查全部子程式輸入參數值;決定如何處理非法參數。

5.6.3 異常情況處理

       應該預先設計好異常處理措施來注意意想不到的情況,異常處理措施應該能使意外情況的出現在開發階段變得非常明顯,而在運作階段又是可以修複的。

5.6.4 預計改動

        越是可能的改動,越是要容易進行,把你在其中預想到的改動域隐含起來,是減少由于改動而對程式帶來影響的最有力武器之一。

5.6.5 計劃去掉調試幫助

        調試幫助措施包括:斷言、記憶體檢查報告、列印語句等及其它一些為友善調試而編寫的代碼。

避免調試資訊混在程式的幾種方法:1)使用版本控制;2)使用内部預處理程式;3)編寫自己的預處理程式;4)保留使用調試程式。

5.6.6 盡早引入調試輔助工具

        越早引入調試輔助工具,它們所起的作用也就會越大。

5.6.7 使用“防火牆”包容錯誤帶來的危害

       “防火牆”技術是一種包容危害政策。

        資訊隐蔽可以幫助在程式中建立防火牆。

        松散的耦合也是在程式内部修建防火牆的手段之一。

        在程式中建防火牆的最好辦法是把某些接口辨別成“安全”區邊界。

5.6.8 檢查函數傳回值

        如果調用了一個函數,并且可以忽略函數傳回值(例如,在C語言中,甚至不需要知道函數是否傳回一個值),千萬不要忽略這個傳回值;防錯性設計的核心就是防止常錯誤。

5.6.9 在最終軟體中保留多少防錯性程式設計

        在最終軟體中應該保留哪些防錯性程式設計的一些原則:1)保留查找重要錯誤的代碼;2)去掉那些無關緊要錯誤的代碼;3)去掉那些引起程式終止的代碼;4)保留那些可以使程式延緩終止的代碼;5)保證留在程式中的錯誤提示資訊是友好的;6)要對防錯性程式設計提高警惕。

5.7 子程式參數

        程式中39%的錯誤都是内部接口錯誤,即子程式間的通信錯誤;以下是盡量減少這類錯誤的一些準則:1)確定實際參數與形式參數比對;2)按照輸入—修改—輸出的順序排列參數;3)如果幾個子程式使用了相似的參數,應按照不變的順序排列這些參數;4)使用所有的參數;5)把狀态和“錯誤”變量放在最後;6)不要把子程式中的參數當作工作變量;7)說明參數的接口假設;8)應該把一個子程式中的參數個數限制在7個左右;9)考慮建一個關于輸入、修改和輸出參數的命名約定;10)僅傳遞子程式需要的那部分結構化變量;11)不要對參數傳遞作出任何設想。

5.8 使用函數

        函數是傳回一個值的子程式,而過程則是不傳回值的子程式。

5.8.1 什麼時候使用函數,什麼時候使用過程

        公用程式設計法是指把一個函數當作過程來使用,并傳回一個狀态變量。

5.8.2 由函數帶來的獨特危險

        使用函數産生了可能不恰當值的危險,這常常是函數有幾條可能的路徑,而其中一條路徑又沒有傳回一個值時産生的;在建立一個函數時,應該在心中執行每一條路徑,以确認函數在所有情況下都可以傳回一個值。

5.9 宏子程式

        特殊情況下,用預處理程式宏調用生成子程式;把宏指令表達式括在括号中。

5.9.1 檢查表

        高品質的子程式:總體問題、防錯性程式設計、參數傳遞問題。

5.10 小結

(1) 建立子程式的最重要原因是加強可管理性(即降低複雜性),其它原因還有節省空間、改進正确性、可靠性、可修改性等等。

(2) 強調強内聚性和松散耦合的首要原因是它們提供了較高層次的抽象性,你可以認為一個具備這種特性的子程式運作是獨立的,這可以使你集中精力完成其它任務。

(3) 有些情況下,放入子程式而帶來巨大收益的操作可能是非常簡單的。

(4) 子程式的名稱表明了它的品質,如果名稱不好但卻是精确的,那麼說明它的設計也是非常令人遺憾的;如果一個子程式的名稱既不好又不精确,那它根本就無法告訴你程式作了些什麼;無論哪種情況,都說明程式需要改進。

(5) 防錯性程式設計可以使錯誤更容易被發現和修複,對最終軟體的危害性顯著減小。

        本章小結:

        本章介紹了高品質子程式特點,包括:名稱恰當、強内聚性、松散耦合性、進行防錯性程式設計等。

        我們編寫任何代碼的一個目的,都是為了確定程式的高品質。幾乎所有的企業都在說:品質是企業的生命。對于IT公司來說,這就展現在了開發人員所編寫的代碼的品質上面。

        一般而言,産品的品質與代碼的品質是成正比的。為了傳遞給使用者可靠的産品,本章中所涉及到的知識都可以派上用場。

繼續閱讀