代碼大全2

代碼大全(二)

 

第1部分  打好基礎

本部分內容

        第1章    歡迎進入軟件構建的世界

        第2章    用隱喻來更充分地理解軟件開發

        第3章    三思而後行:前期準備

        第4章    關鍵的“構建”決策

一、歡迎進入軟件構建的世界

內容:

        什麼是軟件構建

        軟件構建爲何如此重要

        如何閱讀本書

你一定知道“構建”一詞在軟件開發領域之外的含義。“構建”就是“建築工人(construction workers)”在建設一棟房屋、一所學校、及至一座摩天大樓時所做的工作,在你年輕時,可能也曾用“硬紙板(construction paper)”構建過什麼東西。按照一般的用法,“構建”是指建設的過程。構建過程可能包含有計劃、設計、檢查工作的一些方面,但在多數時候,“構建”就是指創建事物過程中動手的那些部分。

1、什麼是軟件構建
   開發計算機已是一個複雜的過程。在過去25年間,研究者已經認識到在軟件開發過程中的各種不同的活動(activity):
     定義問題
     需求分析
     規劃構建
     軟件架構
     詳細設計
     編碼與調試
     單元測試
     集成測試
     集成
     系統測試
     保障維護
構建活動主要關注於編成與調試,但也包含詳細設計、單元測試、集成測試以及其他一些活動
構建活動中的具體任務(task)
 驗證有關的基礎工作已經完成,因此構建活動可以順利地進行下去。
 確定如何測試所寫的代碼
 設計江編寫類(class)和子程序(routine)
 創建並命名變量(variable)和具名常量(named constant)
 選擇控制結構(control structure),組織語句塊
 對你的代碼進行單元測試和集成測試,並排除其中的錯誤
 評審開發團隊其他成員的底層設計和代碼,並讓他們評審你的工作
 潤飾代碼,仔細進行代碼的格式化和註釋
 將單獨開發的多個軟件組件集成爲一體
 調整代碼(tuning code),讓它更快、更省資源

非構建活動主要包括管理(management)、需求分析、軟件架構設計、用戶界面設計、系統測試、以及維護。

2 、軟件構建爲何如此重要
   
構建活動是軟件開發的主要組成部分
    構建活動是軟件開發的核心活動
    把主要精力集中於構建活動,可以大大提高程序員的生產效率
    構建活動的產物——源代碼——往往是對軟件的唯一精確描述
    構建活動是唯一一項確保會完成的工作

要點:
 1、軟件構建是軟件開發的核心活動;構建活動是每個項目中唯一一項必不可少的工作。
 2、軟件構建的主要活動包括;詳細設計、編碼、調試、集成、開發者測試(developer testing)(包括單元測試和集成測試)。
 3、構建也常被稱作“編碼”和“編程”。
 4、構建活動的質量對軟件的質量有着實質性的影響。
 5、最後,你對“如何進行構建”的理解程序,決定了你這名程序員的優秀程序——這就是本書其餘部分的主題了。

二、用隱喻來更充分地理解軟件開發

內容:

        隱喻的重要性

        如何使用軟件隱喻

        常見的軟件隱喻

    計算機科學領域中有着所有學科中最爲豐富多彩的語言。你走進一間安全嚴密、溫度精確控制在20度的房間,並在裏面發現了病毒、特洛伊木馬、蠕蟲、臭蟲、邏輯炸dan、崩潰、論壇口水戰、雙絞線轉換頭,還有致命錯誤......,這此形象的隱喻(比喻)描述了軟件領域中各種特定的現象和事物。像這樣生動活潑的隱喻還能夠描述更加廣泛的現象。藉助這些隱喻,我們能更深刻地理解軟件開發的過程。

2、如何使用軟件隱喻

        與其說一個軟件隱喻像是一張路線圖,還不如說它是一盞探照燈。它不會告訴你到哪裏去尋找答案 ,而僅是告訴你該如何尋找答案。隱喻的作用更像是啓示(啓發、試探法),而不是算法。

        算法是一套定義明確的指令,使你能完成某個特定的任務。算法是可預測的、確定性的、不易變化的。一個告訴你如何從A點到達B點的算法,不會讓你繞路,不會讓你額外地經過D、E、F等地方,更不會讓你停下來聞聞玫瑰花或喝杯咖啡。

        而啓發式方法是一種幫你尋求答案的技術,但它給出的答案是具有偶然性的,因爲啓發式方法僅僅告訴你該如何去找,而沒有告訴你要找什麼,它並不告訴你該如何直接從A點到達B點,它甚至可能連A點和B點在哪裏都不知道。實際上,啓發式方法是穿着小丑兒外套的算法;它的結果不太好預測,也更有趣,但不會給你什麼30天無效退款的保證。

3、常見的軟件隱喻

        軟件中的書法:寫作代碼

        軟件的耕作法:培植系統

        軟件的牡蠣養殖觀點:系統生長

        軟件構建:建造軟件

        組合各個隱喻

要點:

        1、隱喻是啓示而不是算法。因此它們往往有一點隨意

        2、隱喻把軟件開發過程與其他你熟悉的活動聯繫在一起,幫助你更好地理解。

        3、有些隱喻比其他一些隱喻更貼切。

        4、通過把軟件的構建過程比作是房屋的建設過程,我們可以發現,仔細的準備是必要的,而大型項目和小型項目之間也是有差異的。

        5、通過把軟件開發中的實踐比作是智慧工具箱的工具,我們又發現,每位程序員都有許多工具,但並不存在一個能適用於所有工作的工具,因地制宜地選擇正確工具是成爲能有效編程的程序員的關鍵。

        6、不同隱喻彼此並不排斥,應當使用對你最有益處的某種隱喻組合。

三、三思而後行:前期準備

內容:

        前期準備的重要性

        辨明你所從事的軟件的類型

        問題定義的先決條件

        需求的先決條件

        架構的先決條件

        花費在前期準備上的時間長度

    在開始建造房子之前,施工人員會審視藍圖(包含所有細節信息的設計詳圖),查看是否獲得了全部(建築)許可證,並測量房屋的地基。施工人員建造摩天大樓是一種方法,建造普通住宅用另一種方法,建造犬舍用第三種方法。無論何種項目,都會對“準備工作”進行剪裁,使之符合項目的特定需要;在構建活動開始之有,準備工作要做周全。

    本章描述軟件構建必須做的準備工作。就像修建建築物一樣,項目的成敗很大程度上在構建活動開始之前就已經註定了。如果地基沒打好,或者計劃不充分,那麼你在構建期間能做的無非是儘量讓損害最小罷了。

1、前期準備的重要性

        使用高質量的實踐方法是那些能創造高質量軟件的程序員的共性。這些高質量的實踐方法在項目的初期、中期、末期都強調質量。

        如果你在項目的末期強調質量,那麼你會強調系統測試

        如果你在項目的中期強調質量,那麼你會強調構建實踐

        如果你在項目的初期強調質量,那麼你就會計劃、要求並且設計一個高質量的產品。

    關於開始構建之前要做前期準備的絕對有力且簡明的論據

        設想你已經到過“問題定義”之山,與名爲“需求”之人同行數裏,在“架構”之泉面前脫下髒兮兮的外套,然後在“前期準備”之純淨水中沐浴。那麼你就會知道,在實現一個系統之前,你需要理解“這個系統應該做什麼”,以及“它該如何做這些”。

2、辨明你所從事的軟件的類型

        不同種類的軟件項目,需要在“準備工作”和“構建活動”之間做出不同的平衡。三種常見的軟件項目種類:商業系統(Intenet站點)、使命攸關的系統(軟件工具)、性命攸關的嵌入式系統(航空軟件)

開發商業系統的項目往往受益於高度迭代的開發法,這種方法的“計劃、需求、架構”活動與“構建、系統測試、質量保證”活動交織在一起。性命攸關的系統往往需求採用更序列式的方法——“需求穩定”是確保“超高等級的可靠性”的必備條件之一。

    迭代的開發法:隨着着項目的進展不斷找出錯誤,在項目進行過程中一點點地吸收消化返工,這樣使得總體成本較低。

   序列式的方法:僅僅依賴於測試來發現缺陷,將絕大部分缺陷修正工作推遲到項目結束的時候進行,使得成本較高。

3、問題定義的先決條件

        在開始構建之前,首先要滿足的一項先決條件是,對這個系統是解決的問題做出清楚的陳述。這有時稱爲“產品設想”、“設想陳述”、“任務陳述”或者“產品定義”。這裏將它稱爲“問題定義”。

        “問題定義”只定義了“問題是什麼”,而不涉及任何可能的解決方案。這是一個很簡單的陳述,可能只有一到時兩項,並且聽起來應該你一個問題。像“我們跟不上Gigatron的訂單了”這樣的句子聽起來你是個問題,而且確實是一個很好的問題定義。而“我們需要優化數據自動採集系統,使之跟上Gigatron的訂單”這種句子是糟糕的問題定義。它聽起來不像是問題,倒像是解決方案。

        問題定義應該用客戶的語言來書寫,而且應該從客戶的角度來描述問題。

        “未能定義問題”的處罰是,你浪費了大量時間去解決錯誤的問題。這是雙重處罰,因爲你也沒有解決正確的問題。

 4、需求的先決條件

        “需求”詳細描述軟件系統應該做什麼,這是達成解決方案的第一步。“需求”活動也稱爲“需求開發”、“需求分析”,“分析”、“需求定義”、“軟件需求”、“規格書”、“功能規格書”、“規格”

    爲什麼要有正確的需求:明確的需求有助於確保用戶(而不是程序員)駕馭系統的功能。如果沒有好的需求,你可能對問題有總體的把握,但卻沒有擊中問題的特定方面。

    在構建期間處理需求變量:

        使用本節末尾的需求覈對表來評估你的需求的質量

        確保每一個人都知道需求變量的代價

        建立一套變量控制程序

        使用能適應變量的開發方法

        放棄這個項目

        注意項目的商業案例

覈對表:需求

        這張需求覈對表包含了一系列的問題——問問自己項目的需求工作做得如何。在開始構建之前,用這份列表做一次“心智健全”檢查,看看你的地基到底有多堅固——用“需求里氏震級”來衡量。

        針對功能需求
            1、是否詳細定義了系統的全部輸入,包括其來源、精度、取值範圍、出現頻率等?
            2、是否詳細定義了系統的全部輸出、包括目的地、精度、取值範圍、出現頻率、格式等?
            3、是否詳細定義了所有輸出格式(Web頁面、報表,等等)?
            4、是否詳細定義了所有硬件及軟件的外部接口?
            5、是否詳細定義了全部外部通信接口,包括握手協議、糾錯協議、通信協議等?
            6、是否列出了用戶想要做的全部事情?
            7、是否詳細定義了每個任務所用的數據,以及每個任務得到的數據?
        針對非功能需求(質量需求)
            1、是否爲全部必要的操作,從用戶的視角。詳細描述了期望響應時間?
            2、是否詳細描述了其他與計時有關的考慮,例如處理時間、數據傳輸率、系統吞吐量?
            3、是否詳細定義了安全級別?
            4、是否詳細定義了可靠性,包括軟件失靈後的後果、發生故障時需要保護的至關重要的信息、錯誤檢測與恢復的策略等?
            5、是否定義了機器內存和剩餘磁盤空間的最小值?
            6、是否詳細定義了系統的可維護性,包括適應特定功能的變更、操作環境的變更、與其他軟件接口的變更能力?
            7、是否包含對“成功”的定義?“失敗”的定義呢?

        需求的質量
            1、需求是用用戶的語言書寫的嗎?用戶也這麼認爲嗎?
            2、每條需求都不與其它需求衝突嗎?
            3、是否詳細定義了相互競爭的特性之間的權衡——例如,健壯性與正確性之間的權衡?
            4、是否避免在需求中規定設計(方案)?
            5、需求是否在詳細程序上保持相當一致的水平?有些需求應該更詳細地描述嗎?有些需求應該更粗略地描述嗎?
            6、需求是否足夠清晰,即使轉交給一個獨立的小組去構建,他們也能理解嗎?開發者也這麼想嗎?
            7、每個條款都與待解決的問題及其解決方案相關嗎?能從每個條款上溯到它在問題域中對應的根源嗎?
            8、是否每條需求都是可測試的?是否可能進行獨立的測試,以檢驗滿不滿足各項需求?
            9、是否詳細描述了所有可能對需求的改動,包括各項改動的可能性?
       需求的完備性
            1、對於在開始開發之前無法獲得的信息,是否詳細描述了信息不完全的區域?
            2、需求的完備度是否能達到這種程度:如果產品滿足所有需求,那麼它就是可接受的?
            3、你對全部需求都感到很舒服嗎?你是否已經去掉了那些不可能實現的需求——那些只是爲了安撫客戶和老闆的東西?

四、關鍵的“構建”決策
內容:
        1、選擇編程語言
        2、編程約定
        3、你在技術浪潮中的位置
        4、選擇主要的構建實踐方法
    第3章“三思而後行,前期準備”討論了設計藍圖和建築許可證在軟件業裏的等價物。你可能對那此準備工作沒有多少發言權,所以第3章關注的焦點是確定“當構建開始後你需要做什麼”。
    本章關注的焦點是程序員和技術帶頭人個人必須(直接或間接)負責的準備工作。在向工地進發之前,如何選擇適用的工具別在你的腰帶上,你的手推車裏該裝哪些東西?本章討論的就是這些事務在軟件中的等價物。
1、選擇編程語言
        研究表明,編程語言的選擇從多個方面影響生產率和代碼質量。
2、編程約定
        在高質量軟件中,你可以看到“架構的概念完整性”與“其底層實現”之間的關係。“實現”必須與(指導該實現的)“架構”保持一致,並且這種一致性是內在的、固有的。這正是變量名稱、類的名稱、子程序名稱、格式約定、註釋約定等這些針對“構建活動”的指導方針的關鍵所在。
3、你在技術浪潮中的位置
         在成熟的技術環境下——浪潮的末尾,21世紀最初10所的中期的網絡編程——我們受益於豐富的軟件開發基礎設施。
         在技術浪潮的前期——20世紀90年代中期的網絡編程——情況正好相反,可選擇的編程語言非常少。
        “深入一種語言去編程”的程序員首先決定他要表達的思想是什麼,然後決定如何使用特定語言提供的工具來表達這些思想。
4、選擇主要的構建實踐方法
         “構建”有一部分準備工作,就是決定在這麼多的可選的實踐方法中,你想要強調哪些。下面的覈對表總結了在“構建”過程中,應該有意識地使用或者排斥的特定編程實踐。
覈對表:主要的構建實踐
    編碼
        你有沒有確定,多少設計工作將要預先進行,多少設計工作在鍵盤上進行(在編寫代碼的同時)?
        你有沒有規定諸如名稱、註釋、代碼格式等“編碼約定”?
        你有沒有規定的由軟件架構確定的編碼實踐,比如如何處理錯誤條件、如何處理安全性事項、對於類接口有哪些約定、可重用的代碼遵循哪些標準、在編碼時考慮多少性能因素等?
        你有沒有找到自己在技術浪潮中的位置,並相應調整自己的措施?如果必要,你是否知道如何“深入一種語言去編程”,而不受限於語言(僅僅“在一種語言上編程”)?

   團隊工作
        你有沒有定義一套集成工序——即,你有沒有定義一套特定的步驟,規定程序員在把代碼check in(簽入)到主源碼(代碼庫)中之前,必須履行這些步驟?
        程序員是結對編程、還是獨自編程,或者這二者的某種組合?
    質量保證
        程序員在編寫代碼之前,是否先爲之編寫測試用例?
        程序員會爲自己的代碼寫單元測試嗎(無論先寫還是後寫)?
        程序員在check in代碼之前,會用調試器單步跟蹤整個代碼流程嗎?
        程序員在check in代碼之前,是否進行集成測試?
        程序員會複審(review)或檢查別人的代碼嗎?
    工具
        你是否選用了某種版本控制工具?
        你是否選定了一種語言,以及語言的版本或編譯器版本?
        你是否選擇了某個編程框架(framework,如J2EE或Microsoft.NET),或者明確地決定不使用編程框架?
        你是否決定允許使用非標準的語言特性?
        你是否選定並擁有了其他將要用到的工具——編輯器、重構工具、調試器、測試框架(test framework)、語法檢查器等?

要點:
        1、每種編程語言都有其優點和弱點。要知道你使用的語言的明確優點和弱點。
        2、在開始編程之前、做好一些約定。“改變代碼使之符合這些約定”是近乎不可能的。
        3、“構建的實踐方法”的種類比任何單個項目能用到的要多。有意識地選擇最適合你的項目的實踐方法。
        4、問問你自己,你採用的編程實踐是對你所用的編程語言的正確響應,還是受它的控制?請記得“深入一種語言去編程”,不要僅“在一種語言上編程”。
        5、你在技術浪潮的位置決定了哪種方法是有效的——甚至是可能用到的。確定你在技術浪潮中的位置,並相應調整計劃和預期目標。

       

 第2部分  創建高質量的代碼

五、軟件構建中的設計

       理想的設計特徵

         高質量的設計具有很多常見特徵。如果你能實現所有這些目標,你的設計就真的非常好了。這些目標之間會相互抵觸,但這也正是設計中的挑戰所在———在一系列相互竟爭的目標之中做出一套最好的折中方案。有些高質量設計的特徵也同樣是高質量程序的特徵,如可靠性和性能等。而有些則是設計範疇內的特徵。

        下在就列出一些設計範疇內的特徵:

         最小的複雜度:正如剛剛說過的,設計的首要目標就是要讓複雜度最小。要避免做出“聰明的”設計,因爲“聰明的”設計常常都是難以理解的。應該做出簡單且易於理解的設計。如果你的設計方案不能讓你在專注於程序的一部分時安心地忽視其他部分的話,這一設計就沒有什麼作用了。

          易於維護:易於維護意味着在設計時爲做維護工作的程序員着想。請時刻想着這些維護程序員可能會就你寫的代碼而提出的問題。把這些程序員當成你的聽衆,進而設計出能自明的系統來。

          鬆散耦合:鬆散耦合意味着在設計時讓程序和各個組成部分之間關聯最小。通過應用類接口中的合理抽象、封裝性及信息隱藏等原則,設計出相互關聯儘可能最少的類。減少關聯也就減少了集成、測試與維護時的工作量。

          可擴展性:可擴展性是說你能增強系統的功能而無須破壞其底層結構。你可以改動系統的某一部分而不會影響到其他部分。越是可能發生的改動,越不會給系統造成什麼破壞。

          可重用性:可重用性意味着所設計的系統的組成部分能在其他系統中重複使用。

          高扇入:高扇入就是說讓大量的類使用某個給定的類。這意味着設計出的系統很好地利用了在較低層次上的工具類(utility classes)。

          低扇出:低扇出就是說一個類裏少量或適中地使用其他的類。高扇出(超過約7個)說明一類使用了大量其他的類,因此可以變得過於複雜。研究發現,無論考慮某個子程序調用其他子程序的量,還是考慮某個類使用其他類的量,低扇出的原則都是有益的。

          可移植性:可移植是說應該這樣設計系統,使它能很方便地移植到其他環境中。

          精簡性:精簡性意味着設計出的系統沒有多餘的部分,伏爾泰曾說,一本書的完成,不在它不能再加入任何內容的時候,而在不能再刪去任何內容的時候。在軟件領域中,這一觀點就更正確,因爲任何多餘的代碼也需要開發、複審和測試,並且當修改了其他代碼之後還要重新考慮它們。軟件的後續版本也要和這些多餘代碼保持向後兼容。要問這個關鍵的問題:“這雖然簡單,但把它加進來之後會損害什麼呢”?

          層次性:層次性意味着儘量保持系統各個分解層次的層次性,使你能在任意的層面上觀察系統,並得到某種具有一致性的看法。設計出來的系統應該能在任意層次上觀察而不需要進入其他層次。舉例來說,假設你正在編寫一個新系統,其中用到很多設計不佳的舊代碼,這時你就應該爲新系統編寫一個負責同舊代碼交互的層。在設計這一層時,要讓它能隱藏舊代碼的低劣質量,同時爲新的層次提供一組一致的服務。這樣,你的系統其他部分就只需與這一層進行交互,而無須直接同舊代碼打交道了。在這個例子中,層次化設計的益處有:(1)它把低劣代碼的爛泥潭禁閉起來;(2)如果你最終能拋棄或者重構舊代碼,那時就不必修改除交互層之外的任何新代碼。

          標準性:一個系統所依賴的外來的、古怪的東西越多,別人在第一次想要理解它的時候就越是頭疼。要儘量用標準化的、常用的方法,讓整個系統給人一種熟悉的感覺。

關於設計啓發的總結

下面是對主要的設計中的啓發式方法的總結:

          尋找現實世界的對象(object,物體)

          形成一致的抽象

          封裝實現細節

          在可能的情況下繼承

          藏住祕密(信息隱藏)

          找出容易改變的區域

          保持鬆散耦合

          探尋通用的設計模式

下列的啓發式方法有時也很有用

          高內聚性

          構造分層結構

          嚴格描述類契約

          分配職責

          爲測試而設計

          避免失誤

          有意識地選擇綁定時間

          創建中央控制點

          考慮使用蠻力

          畫一個圖

          保持設計模塊化

 六、爲什麼有這麼多關於繼承的規則

        下面來總結一下何時可以使用繼承,何時又該使用包含

         1、如果多個類共享數據而非行爲,應該創建這些類可以包含的共用對象。

         2、如果多個類共享行爲而非數據,應該讓它們從共同的基類繼承而來,並在基類裏定義共用的子程序。

         3、如果多個類既共享數據也共享行爲,應該讓它們從一個共同的基類繼承而來,並在基類裏定義共用的數據和子程序。

         4、當你想由基類控件接口時,使用繼承;當你想自己控制接口時,使用包含。

七、高質量的子程序

     什麼是子程序?子程序是爲實現一個特定的目的而編寫的一個可被調用的方法(method)或過程

        7.2 在子程序層上設計

               內聚性:對子程序而言,內聚性是指子程序各種操作之間聯繫的緊密程序。有些程序員更喜歡使用“強度”這一術語。

               功能上的內聚性:是最強也是最好的一種內聚性,也就是說讓一個子程序僅執行一項操作。例如:GetCustomerName(),這樣的子程序都是高度內聚性的。當然,以這種方式來評估內聚性,前提是子程序所執行的操作與其名字相符---如果它還做了其他操作,那麼它就不夠內聚,同時其命名也有問題。

                除此之外,還有其他一些種類的內聚性人們卻通常認爲是不夠理想的。

                順序上的內聚性:是指在子程序內包含有需要按特定順序執行的操作,這些步驟需要共享數據,而且只有在全部執行完畢後才完成了一項完整的功能。

                通信上的內聚性:是指一個子程序中的不同操作使用了同樣的數據,但不在在其他任何聯繫。例如某個子程序先根據傳給它的彙總數據打印一份彙總報表,然後再把這些彙總數據重新初始化,那麼這個子程序就具有通信上的內聚性;因爲這兩項操作只是因爲使用了相同的數據才彼此產生聯繫。

                臨時的內聚性:是指含有一些因爲需要同時執行才放到一起的操作的子程序。典型的例子有;Startup()、Shutdown()、CompleteNewEmployee()等。有些程序員認爲臨時的內聚性是不可取的,因爲它們有時與不良的編程實踐相關----比如說在Startup()子程序裏塞進一大堆互不相關的代碼等。

                過程上的內聚性:是指一個子程序中的操作是按特定的順序進行的。一個例子是依次獲取員工的姓名、住址、和電話號碼的子程序。這些操作執行的順序之所以重要,只是因爲它和用戶按屏幕提示而輸入數據在順序相一致。另一個子程序用來獲取員工的其他數據。這段程序也具有過程上的內聚性,因爲它把一組操作賦以特定的順序,而這些操作並不需要爲了除此之外的任何原因而彼此關聯。

                邏輯上的內聚性:是指若干操作被放入同一個子程序中,通過傳入的控制標誌選擇執行其中一項操作。之所以稱之爲邏輯上的內聚性,是因爲子程序的控制流或所謂“邏輯”是將這些操作放到一起的唯一原因-----它們都被包在一個很大的 if 語句或case語句中,而不是因爲各項操作之間有任務邏輯關聯。認爲是邏輯上的內聚性的標誌屬性就是各項操作之間的關聯,因此,似乎更應稱其爲“缺乏邏輯的內聚性”。

                巧合的內聚性:是指子程序中的各個操作之間沒有任務可以看到的關聯。它也可稱爲“無內聚性”或“混亂的內聚性”。

要點:

           1、創建子程序最主要的目的是提高程序的可管理性,當然也有其他一些好的理由,其中,節省代碼空間只是一個次要原因;提高可文讀性、可靠性和可修改性等原因都更重要一些。

           2、有時候,把一些簡單的操作寫成獨立的子程序也非常有價值。

           3、子程序可以按照其內聚性分爲很多類,而你應該讓大多數子程序具有功能上的內聚性,這是最佳的一種內聚性。

           4、子程序的名字是它的質量的指示器。如果名字糟糕但恰如其分,那就說明這個子程序設計得很差勁。如果名字糟糕而且又不準確,那麼它就反映不出程序是幹什麼的。不管怎樣,糟糕的名字都意味着程序需要修改。

          5、細心的程序員會非常謹慎地使用宏,而且只在萬不得已時才用。

八、防禦式編程

要點:

          1、最終產品代碼中對錯誤的處理方式要比“垃圾進、垃圾出”複雜很多。

          2、防禦式編程技術可以讓錯誤更容易發現、更容易修改,並減少錯誤對產品代碼的破壞。

          3、斷言可以幫助人儘早發現錯誤,尤其是在大型系統和高可靠性的系統中,以及快速變化的代碼中。

          4、關於如何處理錯誤輸入的決策是一項關鍵的錯誤處理決策,也是一項關鍵的高層設計決策。

          5、異常提供了一種與代碼正常流程角度不同的錯誤處理手段。如果留心使用異常,它可以成爲程序員們知識工具箱中的一項有益補充,同時也應該在異常和其他錯誤處理手段之間進行權衡比較。

          6、針對產品代碼的限制並不適用於開發中的軟件。你可以利用這一優勢在開發中添加有助於更快地排查錯誤的代碼。

九、僞代碼編程過程

本章將從微觀上關注編程過程-------也就是關注創建單獨的類及其子程序的特定步驟,這些步驟對任何規模的項目來說都十分關鍵。

        1、創建類和子程序的步驟概述

              創建一個類的步驟

                      創建類的總體設計

                      創建類中的子程序

                      複審並測試整個類

             創建子程序的步驟

                       設計子程序

                       檢查設計

                       編寫子程序的代碼

                       檢查代碼

        2、僞代碼

              僞代碼這個術語是指某種用來描述算法、子程序、類或完整程序的工作邏輯的、非形式的、類似於英語的記法。僞代碼編程過程則是一種通過書寫僞代碼而更高效地創建程序代碼的專門方法。

        3、通過僞代碼編程過程創建子程序

              設計子程序

              編寫子程序的代碼

              檢查代碼

              收尾工作

              按照需要重複上述步驟

        4、僞代碼編程過程的替代方案

              下面是其它專家推薦的一些不同的方法。你即可以用這些方法替代僞代碼編程過程,也可以把它們用作對僞代碼編程過程的補充。

               測試先行開發

               重構

               契約式設計

               東拼西湊

要點:

         1、創建類和子程序通常都是一個迭代的過程。在創建子程序的過程中獲得認識常常會反過來影響類的設計。

         2、編寫好的僞代碼需要使用易懂的英語,要避免使用特定編程語言中才有的特性,同時要在意圖的層面上寫僞代碼(即描述該做什麼,而不是要怎麼去做)。

         3、僞代碼編程過程是一個行之有效的做詳細設計的工具,它同時讓編碼工作更容易。僞代碼會直接轉化爲註釋,從而確保了註釋的準確度和實用性。

         4、不要只停留在你所想到的第一個設計方案上。反覆使用僞代碼做出多種方案,然後選出其中最佳的一種方案再開始編碼。

         5、每一步完成後都要檢查你的工作成果,還要鼓勵其他人幫你檢查。這樣你就會在投入精力最少的時候,用最低的成本發現錯誤。

第3部分  變量

十、使用變量的一般事項

        1、數據認知

                創建有效數據的第一步是瞭解所要創建數據的種類。

          2、輕鬆掌握變量定義

                關閉隱藏聲明

                聲明全部的變量

                遵循某種命名規則

                檢查變量名

          3、變量初始化原則

                不合理地初始化數據是產生編程錯誤的常見根源之一。

                 在聲明變量的時候初始化

                在靠近變量第一次使用的位置初始化它

                理想情況下,在靠近第一次使用變量的位置聲明和定義該變量。

                在可能的情況下使用final或者const

                在類的構造函數裏初始化該類的數據成員

                檢查是否需要重新初始化

                一次性初始化具名常量:用可執行代碼來初始化變量

                使用編譯器設置來自動初始化所有變量

                利用編譯器的警告信息

                檢查輸入參數的合法性

                使用內存訪問檢查工具來檢查錯誤的指針

                在程序開始時初始化工作內存

          4、作用域

                “作用域”可以看作是一種衡量變量的知名度的方法;

                 使變量引用局部化

                 儘可能縮短變量的“存活”時間

                 減小作用域的一般原則

                 有關縮小變量作用域的說明         

          5、持續性

                “持續性”是對一項數據的生命期的另一種描述。持續性具有多種形態。

          6、綁定時間

                對程序維護和更改有很深遠影響的一個話題就是“綁定時間”,對變量與數據綁定的時間進行一下總結。( 編碼時、編譯時、加載時、對象實例化時、即時),一般而言,綁定時間越早靈性性就會越差,但複雜度也會越低。

          7、數據類型和控制結構之間的關係

                jackson描繪出了三種類型的數據和相應控制結構之間的關係。

                A、序列型數據翻譯爲程序中的順序語句

                B、選擇型數據翻譯爲程序中的 if 和 case 語句

                C、迭代型數據翻譯成程序中的 for、repeat、while等循環結構

          8、爲變量指定單一用途

                通過使用一些巧妙的方法,可以給一個變量賦予多種職責。不過你最好還是遠離這些奇技淫巧。

                每個變量只用於單一用途

                避免讓代碼具有隱含含義

                確保使用了所有已聲明的變量

要點:

          1、數據初始化過程很容易出錯,所以請用本章描述的初始化方法避免由於非預期的初始值而造成的錯誤。

          2、最小化每個變量的作用域。把同一變量的引用點集中在一起。把變量限定在子程序或類的範圍之內。避免使用全局數據。

          3、把使用相同變量的語句儘可能集中在一起。

          4、早期綁定會減低靈活性,但有助於減小複雜度。晚期綁定可以增加靈活性,同時增加複雜度。

          5、把每個變量用於唯一的用途。

十一、變量名的力量

覈對表:變量命名

        名字完整並準確地表達了變量所代表的含義嗎?

        名字反映了現實世界的問題而不是編程語言方案嗎?

        名字足夠長,可以讓你無須苦苦思索嗎?

        如果有計算值限定符,它被放在名字的最後嗎?

        名字中用Count或者Index來代替Num了嗎?

爲特定類型的數據命名

        循環下標的名字有意義嗎(如果循環的長度超出了一兩行代碼或者出現了嵌套循環,那麼就應該是i/j或k以外的其他名字)?

        所有的“臨時”變量都重新命以更有意義的名字了嗎?

        當布爾變量的值爲真時,變量名能準確表達其含義嗎?

        枚舉類型的名字中含有能夠表示其類別的前綴或後綴了嗎?例如,把Color_用於Color_Red,Color_Green,Color_Blue等了嗎?

        具名常量是根據它所代表的抽象實體而不是它所代表的數字來命名的嗎?

命名規則

        規則能夠區分局部數據、類的數據和全局數據嗎?

        規則能夠區分類型名、具名常量、枚舉類型和變量名嗎?

        規則能夠在編譯器不強制檢測只讀參數的語言裏標識出子程序中的輸入參數嗎?

        規則儘可能地與語言的標準規則兼容嗎?

        名字爲了可讀性而加以格式化嗎?

短名字

        代碼用了長名字嗎?

        是否避免只爲了省一個字符而縮寫名字的情況?

        所有單詞的縮寫方式都一致嗎?

        名字能夠讀出來嗎?

        避免使用容易被看錯或者讀錯的名字嗎?

        在縮寫對照表裏對短名字做出說明嗎?

常見命名問題:你避免使用......

        容易讓人誤解的名字

        有相近含義的名字

        只有一兩個字符不同的名字

        發音相近的名字

        包含數字的名字

        爲了縮短而故意拼錯的名字

        英語中經常拼錯的名字

        與標準子程序名或者預定義變量名衝突的名字

        過於隨意的名字

        含有難讀的字符的名字

要點:

       1、 好的變量名是提高程序的可讀性的一項關鍵要素。對特殊種類的變量,比如循環下標和狀態變量,需要加以特殊的考慮。

       2、 名字要儘可能地具體。那些太模糊或者太通用以致於能夠用於多和目的的名字通常都是很不好的。

       3、命名規則應該能夠區分局部數據、類數據和全局數據。它們還應當可以區分類型名、具名常量、枚舉類型名字和變量名。

       4、無論做哪種類型項目,你都應該採用某種變量命名規則。你所採用的規則的種類取決於你的程序的規模,以及項目成員的人數。

       5、現代編程語言很少需要用到縮寫。如果你真的要使用縮寫,請使用項目縮寫詞典或者標準前綴來幫助理解縮寫。

       6、代碼閱讀的次數遠遠多於編寫的次數。確保你所取的名字更側重於閱讀方便而不是縮寫方便。

十二、基本數據類型

覈對表:基本數據類型

數值概論

        代碼中避免使用神祕數值嗎?

        代碼考慮了除零錯誤嗎?

        類型轉換很明顯嗎?

        如果在一條語句中存在兩個不同類型的變量,那麼這條語句會像你期望的那樣求值嗎?

        代碼避免了混合類型比較嗎?

        程序編譯時沒有警告信息嗎?

整數

        使用整數除法的表達式能按預期的那樣工作嗎?

        整數表達式避免整數溢出問題嗎?

浮點數

        代碼避免了對數量級和相差大的數字做加減運算嗎?

        代碼系統地阻止了舍入錯誤的發生嗎?

        代碼避免對浮點數做等量比較嗎?

字符和字符串

        代碼避免使用神祕字符和神祕字符串嗎?

        使用字符串時避免了off-by-one(緩衝溢出錯誤) 錯誤嗎?

        C代碼把字符串指針和字符數組區別對待了嗎?

        C代碼遵循了把字符串聲明爲CONSTANT + 1 長度的規則了嗎?

        C代碼在適當的時候用字符數組來代替指針了嗎?

        C代碼把字符串初始化爲NULL來避免無終端的字符串了嗎?

        C代碼用strncpy()代替strcpy()嗎?strncat()和strncmp()呢?

布爾變量

        程序用額外的布爾變量來說明條件判斷了嗎?

        程序用額外的布爾變量來簡化條件判斷了嗎?

枚舉類型

        程序用枚舉類型而非具名變量來提高可讀性、可靠性和可修改性嗎?

        當變量的用法不能僅用true和false表示的時候,程序用枚舉類型來取代布爾變量了嗎?

        針對枚舉類型的測試檢測了非法數值嗎?

        把枚舉類型的第一項條目保留爲“非法的”了嗎?

具名常量

        程序用具名常量而不是神祕數值來聲明數據和表示循環界限嗎?

        具名常量的使用一致嗎?--------沒有在有些位置使用具名常量又在其他位置使用文字量?

數組

        所有的數組下標都沒有超出數組邊界嗎?

        數組引用沒有出現 off-by-one 錯誤嗎?

        所有多維數組的下標的順序都正確嗎?

        在嵌套循環裏,把正確的變量用於數組下標來避免循環下標串話了嗎?

創建類型

        程序對每一種可能變化的數據分別採用不同的類型嗎?

        類型名是以該類型所表示的現實世界實體爲導向,而不是以編程語言類型爲導向的嗎?

        類型名的描述性足以強,可以幫助解釋數據聲明嗎?

        你避免重新定義預定義類型嗎?

        與簡單地重定義一個類型相比,你考慮過創建一個新類嗎?

要點:

        1、使用特定的數據類型就意味着要記住適用於各個類型的很多獨立的原則。用本章的覈對表來確認你已經對常見問題做了考慮。

        2、如果你的語言支持,創建自定義類型會使得你的程序更容易修改,並更具有自描述性。

        3、當你用typedef或者其等價方式創建了一個簡單類型的時候,考慮是否更應該創建一個新的類。

十三、不常見的數據類型

內容:

        結構體:這一術語指的是使用其他類型組建的數據。

        指針:指針的使用是現代編程中最容易出錯的領域之一,以至於你java\c#和VB這些沒有提供指針數據類型。

        全局數據:全局數據可以在程序中任意一個位置訪問。作用域比局部變量更廣的變量。

 覈對表:使用不常見數據類型的注意事項

 結構體

        你使用結構體而不是使用單純的變量來組織和操作相關的數據嗎?

        你考慮創建一個類來代替使用結構體嗎?

全局數據

        所有的變量是否都是局部的或者是類範圍的?除非絕對有必要纔是全局的?

        變量的命名規則能把局部數據、類數據和全局數據區分開嗎?

        你對所有的全局變量都加以文檔說明了嗎?

        避免使用僞全局數據,即被四處傳遞且包含有雜亂數據的巨大對象嗎?

        用訪問器子程序來取代全局數據嗎?

        把訪問器子程序和數據組織到類裏面嗎?

        訪問器子程序提供了一個在底層數據類型實現之上的抽象層了嗎?

        所有相關的訪問器子程序都位於同一抽象層之上嗎?

指針

        把指針操作隔離來子程序裏嗎?

        指針引用合法嗎?或者說指針有可能成爲空懸指針嗎?

        代碼在使用指針之前檢查它的有效性嗎?

        在使用指針所指向的變量之前檢查其有效性嗎?

        指針用完後被設置爲空值嗎?

        就可讀性而言,代碼用了所有需要使用的指針變量嗎?

        鏈表中的指針是按正確的順序加以釋放嗎?

        程序分配了一片保留的內存後備區域,以便在耗盡內存的時候能夠優雅地退出嗎?

        是不是在沒有其他方法可用的情況下最終才使用指針的?

要點:

        1、結構體可以使得程序更簡單、更容易理解,以及更容易維護。

        2、每當你打算使用結構體的時候,考慮採用類是不是會工作的更好。

        3、指針很容易出錯。用訪問器子程序或類以及防禦式編程實踐來保護自己的代碼。

        4、避免用全局變量,不只是因爲它們很危險,還是因爲你可以用其他更好的方法來取代它們。

        5、如果你不得不使用全局變量,那麼就通過訪問器子程序來使用它。訪問器子程序能爲你帶來全局變量所能帶來的一切優點,還有一些額外好處。

第4部分 語句

十四、組織直線型代碼

覈對表:組織直線型代碼

        代碼使得語句之間的依賴關係變得明顯嗎?

        子程序的名字使得依賴關係變得明顯嗎?

        子程序的參數使得依賴關係變得明顯嗎?

        如果依賴關係不明確,你是否用註釋進行了說明嗎?

        你用“內務管理變量”(housekeeping variables)來檢查代碼中關鍵位置的順序依賴關係了嗎?

        代碼容易按照自上而下的順序閱讀了嗎?

        相關的語句被組織在一起嗎?

        把相對獨立的語句組放進名自的子程序裏嗎?

要點:

        1、組織直線型代碼的最主要原則是按照依賴關係進行排列。

        2、可以用好的子程序名、參數列表、註釋、以及------如果代碼足夠重要------內容管理變量來讓依賴關係變得更明顯。

        3、如果代碼之間沒見有順序依賴關係,那就設法使相關的語句儘可能地接近。

十五、使用條件語句

覈對表:使用條件語句

 if-then語句

        代碼的正常路徑清晰嗎?

        if-then測試對等量分支的處理方式正確嗎?

        使用了else子句並加以說明嗎?

        else子句用得對嗎?

        用對了if和else子句,即沒把它們用反嗎?

        需要執行的正常情況是位於if而不是else子句裏嗎?

if-then-else-if語句

        把複雜的判斷封閉到布爾函數調用裏了嗎?

        先判斷最常見的情況了嗎?

        判斷包含所有的情況嗎?

        if-then-else-if是最佳的實現嗎?比case語句還要好嗎?

case語句

        case子句排序得有意義嗎?

        每種情況的操作簡單嗎?必要的時候調用了其他子程序嗎?

        case語句檢測的是一個真實的變量,而不是一個只爲了濫用case語句而刻意製造變量嗎?

        默認子句用的合法嗎?

        用默認子句來檢測和報告意料之外的情況了嗎?

        在C、C++或者Java裏,每一個case的末尾都有一個break嗎?

要點:

       1、 對於簡單的if-else語句,請注意if子句和else子句的順序,特別是用它來處理大量錯誤的時候。要確認正常的情況是清晰的。

        2、對於if-then-else語句串和case語句,選擇一種最有利於閱讀的排序。

        3、爲了捕捉錯誤,可以使用case語句中的default子句(默認子句),或者使用if-then-else語句串中的最後那個else子句。

        4、各種控制結構並不是生來平等的。請爲代碼的每個部分選用最合適的控制結構。

十六、控制循環

內容:

        選擇循環的種類:

        循環控制:

        輕鬆創建循環——由內而外

        循環和數組的關係:

覈對表:循環

        循環的選擇和創建

        在合適的情況下用while循環取代for循環了嗎?

        循環是由內到外創建的嗎?

        進入循環

        是從循環頭部進入的循環的嗎?

        初始化代碼是直接位於循環前面嗎?

        循環是無限循環或者事件循環嗎?它的結構是否清晰?

        避免使用像for i = 1 to 9999 這樣的代碼嗎?

        如果這是一個C++\C\或Java中的for循環,那麼把循環頭留給循環控制代碼了嗎?

        循環的內部

        循環是否用了“{ }”或其等價物來括上循環體,以防止因修改不當而出錯嗎?

        循環體內有內容嗎?它是非空的嗎?

        把內務處理集中地放在循環開始或者循環結束處了嗎?

        循環像定義良好的子程序那樣只執行一件操作嗎?

        循環短得足以一目瞭然嗎?

        循環的嵌套層次不多於3層嗎?

        把長循環的內容提取成單獨的子程序嗎?

        如果循環很長,那麼它非常清晰嗎?

        循環下標

        如果這是一個for循環,那麼其中的代碼有沒有隨意修改循環下標值?

        是否把重要的循環下標植保存在另外的變量裏,而不是在循環體外使用該循環下標?

        循環下標是序數類型(整數)或者枚舉類型------而不是浮點類型嗎?

        循環下標的名字有意義嗎?

        循環避免了下標串話問題嗎?

        退出循環

        循環在所有可能的條件下都能終止嗎?

        如果你建立了某種安全計數器標準,循環使用安全計數器了嗎?

        循環的退出條件清晰嗎?

        如果使用了break或者continue,那麼它們用對了嗎?

要點:

        1、循環很複雜。保持循環簡單將有助於別人閱讀你的代碼。

        2、保持循環簡單的技巧包括:避免使用怪異的循環、減少嵌套層次、讓入口和出口一目瞭然、把內務操作代碼放在一處。

        3、循環下標很容易被濫用。因些命名要準確,並且要把它們各自僅用於一個用途。

        4、仔細地考慮循環,確認它在每一種情況下都運行正常,並且在所有可能的條件下都能退出。

十七、不常見的控制結構

內容:

        子程序中的多處返回:

        遞歸:

        goto:

        針對不常見控制結構的觀點:

覈對表:不常見的控制結構

        return

        每一個子程序都僅在有必要的時候才使用return嗎?

        使用return有助於增強可讀性嗎?

        遞歸

        遞歸子程序中包含了停止遞歸的代碼嗎?

        子程序用安全計數器來確保該子程序能停下來嗎?

        遞歸只位於一個子程序裏面嗎?

        子程序的遞歸深度處於程序棧容量可以滿足的限度內嗎?

        遞歸是實現子程序的最佳方法嗎?它要好於簡單的迭代嗎?

        goto

        是否只有在萬不得已時候才使用goto?如果用了goto,是否僅僅是出於增強可讀性和可維護性呢?

        如果是出於效率因素而使用goto,那麼對這種效率上的提升做出衡量並且加以說明了嗎?

        一個子程序裏最多隻用了一個goto標號嗎?

        所有的goto都向前跳轉,而不是向後跳轉嗎?

        所有的goto標號都用到了嗎?

要點:

        1、多個return可以增強子程序的可讀性和可維護性,同時可以避免產生很深的嵌套邏輯。但是使用它的時候要多加小心。

        2、遞歸能夠很優雅地解決一小部分問題,對它的使用也要倍加小心。

        3、在少數情況下,goto是編寫可讀和呆維護代碼的最佳方法。但這種情況非常罕見。除非萬不得已,不要使用gogo。 

十八、表驅動法

內容:

        表驅動法使用總則

        直接訪問表

        索引訪問表

        階梯訪問表

        表查詢的其他示例

覈對表:表驅動法

        你考慮過把表驅動法作爲複雜邏輯的替換方案嗎?

        你考慮過把表驅動法作爲複雜繼承結構的替換方案嗎?

        你考慮過把表數據存儲在外部並在運行期間讀入,以便在不修改代碼的情況下就可以改變這些數據嗎?

        如果無法用一種簡單的數組索引(像agc示例中那樣)去訪問表,那麼你把計算訪問鍵值的功能提取成單獨的子程序,而不是在代碼中重複地計算鍵值嗎?

要點:

        1、表提供了一種複雜的邏輯和繼承結構的替換方案。如果你發現自己對某個應用程序的邏輯或者繼承樹關係感到困惑,那麼問問自己是否可以通過一個查詢表加以簡化。

        2、使用表的一項關鍵決策是決定如何去訪問表。你可以採取直接訪問、索引訪問或者階梯訪問。

        3、使用表的另一項關鍵決策是決定應該把什麼內容放入表中。

十九、一般控制問題

內容:

        布爾表達式

        複合語句(語句塊)

        空語句

        馴服危險的深層嵌套

        編程基礎:結構化編程

        控制結構與複雜度

覈對表:控制結構相關事宜

        表達式中用的是true和false,而不是1和0嗎?

        布爾值和true以及false做比較是隱匿進行的嗎?

        對數值做比較是顯示進行的嗎?

        有沒有通過增加新的布爾變量、使用布爾函數和決策表簡化表達式嗎?

        布爾表達式是用肯定形式表達的嗎?

        括號配對嗎?

        在需要用括號來明確的地方都使用括號嗎?

        把邏輯表達式全括起來了嗎?

        判斷是按照數軸順序編寫的嗎?

        如果適當的話,java中的判斷用的是a.equals(b)方式,而沒有用a==b方式嗎?

        空語句表述的明顯嗎?

        用重新判斷部分條件、轉換成if-then-else或者case語句、把嵌套代碼提取成單獨的子程序、換用一種更面向對象的設計或者其他的改進方法來簡化嵌套語句了嗎?

        如果一個子程序的決策點超過10個,那麼能提出不重新設計的理由嗎?

要點:

        1、使布爾表達式簡單可讀,將非常有助於提高你的代碼的質量

        2、深層次的嵌套使得子程序變得難以理解。所幸的是,你可以相對容易地避免這麼做。

        3、結構化編程是一種簡單並且仍然適用的思想:你可以通過把順序、選擇和循環三者組合起來而開發出任何程序。

        4、將複雜度降低到最低水平是編寫高質量代碼的關鍵。

第5部分 代碼改善

 本章部分內容

        第20章 軟件質量概述

        第21章 協同構建

        第22章  開發者測試

        第23章 調試

        第24章  重構

        第25章 代碼調整策略

        第26章 代碼調整技術

二十、 軟件質量概述

內容:

        軟件質量的特性

        改善軟件質量的技術

        不同質量保障技術的相對效能

        什麼時候進行質量保證工作

        軟件質量的普遍原理

覈對表:質量保證計劃

        是否確定出對項目至關重要的特定質量特性了?

        是否讓其他人意識到項目的質量目標了?

        是否能夠區分質量的外在特性和內在特性?

        是否考慮過某些特性與其他特性相互制約或相互促進的具體方式?

        在軟件開發的每一個階段,項目是否要求針對不同錯誤類型使用不同的錯誤檢測技術?

        項目計劃中是否有計劃有步驟地保證了軟件在開發各階段的質量?

        是否使用了某種質量評估方法,並由此確定質量是改善了還是下降了?

        管理層是否能理解爲了質量保證在前期消耗額外成本,目的就是在項目後期減少成本?

要點:

        1、開發高質量代碼最終並沒有要求你付出更多,只是你需要對資源進行重新分配,以低廉的成本來防止缺陷出現,從而避免低價高昂的修正工作。

        2、並非所有的質量保證目標都可以全部實現。明確哪些目標是你希望達到的,並就這些目標和團隊成員進行溝通。

        3、沒見有任何一種錯誤檢測方法能夠解決全部問題,測試本身並不是排除錯誤的最有效方法。成功的質量保證計劃應該使用多種不同的技術來檢查各種不同類型的錯誤。

        4、在構建期間應當使用一些有效的質量保證技術,但在這之前,一些具有同樣強大功能的質量保證技術也是必不可少的。錯誤發現越早,它與其餘代碼的糾纏就越少,由此造成的損失也越小。

        5、軟件領域的質量保證是面向過程的。軟件開發與製造業不一樣,在這裏並不存在會影響最終產品的重複的階段,因此,最終產品的質量受到開發軟件所用的過程的控制。

二十一、協同構建

 內容:

        協同開發實踐概要

        結對編程

        正式檢查

        其他類型的協同開發實踐

結對編程:在進行結對編程的時候,一位程序員敲代碼,另外一位注意有沒有出現錯誤,並考慮某些策略性的問題,例如代碼的編寫是否正確,正在編寫的代碼是否所需等。結對編程最初是由極限編程所普及推廣的,現在已經使用得相當廣泛了。

覈對表:有效的結對編程

        是否已經有一個編碼規範,以便讓程序員始終把精力集中到編程,而不是編碼風格的討論上。

        結對的雙方是否都積極地參與。

        是否避免了濫用結對編程,而是選擇那些能夠從中獲得好處的工作進行結對編程。

        是否有規律地對人員和工作任務進行輪換。

        結對組合是否在開發速度和個性方面互相匹配。

        是否有一個組長專注於項目管理以及與項目外其他人的溝通。

正式檢查:詳查(正式檢查)是一種特殊的複查,種種跡象表明它在偵測缺陷方面特別有效,並且相對測試來說更加經濟合理。

覈對表:有效的詳查

        你是否有一個覈對表,能讓評論員將注意力集中於曾經發生過問題的領域?

        你是否專注於找出錯誤,而不是修正它們?

        你是否考慮制定某些視角或者場景,以幫助評論員在準備工作的時候集中注意力?

        你是否給予評論員足夠的時間在詳查會議之前進行準備,是否每一個人都做了準備?

        是否每一個參與者都扮演一個明確的角色-----主持人、評論員及記錄員等?

        會議是否以某種高效的速度進行?

        會議是否限制在兩個小時以內?

        是否所有詳查會議的參與者都接受了如何進行詳查的針對性培訓,是否主持人接受了有關主持技巧方面的針對性培訓?

        是否將每次詳查所發現的錯誤數據都收集起來,使你能調整本組織以後使用的覈對表?

        是否收集了準備速度和詳查速度方面的數據,以便你去優化以後的準備和詳查工作?

        是否每次詳查中被指派下去的活動都被正確跟進了,無論是通過主持人自己還是一次重新詳查?

        管理層是否理解他們不應該參與詳查會議?

        是否有一個用於保證修正正確性的跟進計劃?

其他類型的協同開發實踐:走查、代碼閱讀、公開演示

二十二、開發者測試

內容:

        開發者測試在軟件質量中的角色

        開發者測試的推薦方法

        測試技巧錦囊

        典型錯誤

        測試支付工具

        保留測試記錄

覈對表:測試用例

        類和子程序所對應的每一項需求是否都有相應的測試用例?

        類和子程序所對應的每一個設計元素是否都有相應的測試用例?

        每行代碼是否被至少一個測試用例所測試?你是否通過計算測試到每行代碼所需的最少測試用例數量來驗證這一點?

        所有已定義--已使用路徑是否至少被一個測試用例測試過了?

        是否測試過那些不太可能正確的數據流模式,例如已定義--已定義,已定義--已退出以及已定義--已銷燬?

        是否有一張常見錯誤列表,並據此編寫測試用例以檢測過去經常出現的錯誤?

        所有的簡單邊界是否都已經測試過了:最大、最小以及off-by-one?

        是否測試了組合邊界------即,多個輸入數據的組合導致輸出數據過小或者過大?

        測試用例是否檢查了數據類型錯誤,例如一個薪水記賬程序裏的僱員數量是負數?

        是否測試了那些中規中矩的典型數值?

        是否測試了最小正常形式?

        是否測試了最大正常形式?

        是否檢查了與舊數據的兼容性?以及是否對舊硬件、舊操作系統版本以及其他舊版本軟件的接口進行了測試?

        測試用例是否容易手工校驗?

要點:

        1、開發人員測試是完整測試策略的一個關鍵部分。獨立測試也很重要,但這一主題超出了本書的範圍。

        2、同編碼之後編寫測試用例相比較,編碼開始之前編寫測試用例,工作量和花費的時間差不多,但是後者可以縮短缺陷--偵測--調試--修改這一週期。

        3、即使考慮到了各種可用的測試手段,測試仍然只是良好軟件質量計劃的一部分。高質量的開發方法至少和測試一樣重要,這包括儘可能減少需求和設計階段的缺陷。在檢測錯誤方面,協同開發的成效至少與測試相當。這此方法所檢測錯誤的類型也各不相同。

        4、你可以根據各種不同的思路來產生很多測試用例,這些思路包括基礎測試、數據流分析、邊界分析、錯誤數據類型以及正確數據類型等。你還可以通過猜測錯誤的方式得到更多的測試用例。

        5、錯誤往往集中在少數幾個容易出錯的類和子程序上。找出這部分代碼,重新設計和編寫它們。

        6、測試數據本身出錯的密度往往比被測試代碼還要高。查找這種錯誤完全是浪費時間,又不能對代碼有所改善,因此測試數據裏面的錯誤更加讓人煩惱。要像寫代碼一樣小心地開發測試用例,這樣才能避免產生這種問題。

        7、自動化測試總體來說是很有用的,也是進行迴歸測試的基礎。

        8、從長遠來看,改善測試過程的最好辦法就是將其規範化,並對其進行評估,然後用評估中獲得的驗證教訓來改善這個過程。

二十三、調試

內容:

        調試概述

        尋找缺陷

        修正缺陷

        調試中的心理因素

        調試工具---明顯的和不那麼明顯的

覈對表:關於調試的建議

尋找缺陷的方法

        使用所有可用數據來構造你的假設

        不斷提煉產生錯誤的測試用例

        在自己的單元測試族中測試代碼

        藉助可以獲得的任何工具

        用不同的方式重現錯誤

        通過產生更多的數據來構造更多的假設

        利用證僞假設的測試結果

        用頭腦風暴的方式找出可能的假設

        在桌上放一個記事本,把需要嘗試的事情列出來

        縮小被懷疑有問題的代碼區域

        對之前出現過問題的子類和子程序保持警惕

        檢查最近修改的代碼

        擴展被懷疑有問題的代碼區域

        採用增量集成

        檢查常見的缺陷

        和其他人一起討論你的問題

        拋開問題休息一下

        在使用快速骯髒調試法的時候,要設置一個時間上限

        列出所有的蠻力調試方法,逐條應用

解決語法錯誤的方法

        不要太信任編譯器信息中給出的行號

        不要太信任編譯器信息

        不要太信用編譯器所給出的第二條出錯信息

        分而治之,各個擊破

        使用具有語法分析功能的編輯器來找出位置錯誤的註釋和引號

修改缺陷的方法

        在動手之前先理解程序

        理解整個程序而非具體問題

        驗證對錯誤的分析

        放鬆一下

        要保存最初的源代碼

        治本,而非治標

        只有當理由充分的時候纔去修改代碼

        一次只做一個改動

        檢查自己所做的修訂

        添加單元測試來暴露代碼中的缺陷

        找出類似的缺陷

調試的一般方法

        你是否會把調試看做是能讓你更好地理解程序、錯誤、代碼質量各解決問題方法的良機?

        你是否會避免採用隨機嘗試查找錯誤或迷信式的調試方法(不聽話的機器,奇怪的編譯器錯誤,月圓時纔會出現的編程語言的隱藏缺陷,失效的數據,忘記做的重要改動,一個不能正常保存程序的瘋狂的編輯器,這種行爲描述爲迷信式的編程)

        你是否假設錯誤是你自己造成的?

        你是否使用了科學的方法將間歇性的錯誤穩定下來?

        你是否使用了科學的方法來尋找缺陷?

        你在尋找缺陷的時候會使用多種不同的方法麼?還是每次都是用相同的方法?

        你會驗證你的修改是否正確麼?

        你會在調試中使用編譯器警告信息、執行性能分析、利用測試框架和交互式調試方法麼?

要點:

        1、調試同整個軟件開發的成敗息息相關。最好的解決之道是使用本書中介紹的其他方法來避免缺陷的產生。然而,花點時間來提高自己的調試技能還是很划算的,因爲優秀和拙劣的調試表現之間的差距至少是10:1。

        2、要想成功,系統化的查找和改正錯誤的方法至關重要。要專注於你的調試工作,讓每一次測試都能讓你朝着正確的方向前進一步。要使用科學的調試方法。

        3、在動手解決之前,要理解問題的根本。胡亂猜測錯誤的來源和隨機修改將會讓你的程序陷入比剛開始調試時更爲糟糕的境地。

        4、將編譯器警告級別設置爲最嚴格,把警告信息所報告的錯誤都改正。如果你忽略了明顯的錯誤,那麼要改正那些微妙的錯誤就會非常麻煩。

        5、調試工具對軟件開發而言是強有力的支持手段。找出這些工具並加以應用,當然,請記得在調試的時候開動腦筋。

二十四、重構

內容:

        軟件演化的類型

        重構簡介

        特定的重構

        安全的重構

        重構策略

重構簡介

覈對表:重構的理由

        代碼重複

        子程序太長

        循環太長或者嵌套太深

        類的內聚性太差

        類的接口的抽象層次一致

        參數表中參數太多

        類的內部修改往往侷限於某個部分

        需要對多個類進行並行修改

        對繼承體系的並行修改

        需要對多個case語句進行並行修改

        相關的數據項只是被放在一起,沒有組織到類中

        成員函數更多使用了其他類的功能,而非自身類的

        過於依賴基本數據類型

        一個類不做什麼事

        一連串傳遞流浪數據的子程序

        中間人對象什麼也不幹

        某個類同其他類關係過於密切

        子程序的命名太差

        數據成員被設置爲公用

        派生類僅僅使用了基類的一小部分成員函數

        用註釋來掩飾拙劣的代碼

        使用了全局變量

        在子程序調用前使用設置代碼,調用後使用收尾代碼

        程序包含的某些代碼似乎在將來某個時候纔會被用到

特定的重構

覈對表:重構總結

 1、數據級的重構

        用具名常量來代替神祕數值

        用更明確或更具有信息量的名字來重命名變量

        將表達式內聯化

        用函數來代替表達式

        引入中間變量

        將多用途變量轉換爲多個單一用途變量

        使用局部變量實現局部用途而不是使用參數

        將基礎數據類型轉化爲類

        將一組類型碼轉化爲類或是枚舉類型

        將一組類型碼轉化爲含派生類的類

        將數組轉化爲對象

        封閉羣集

        用數據類替代傳統記錄

2、語句級的重構

        分解布爾表達式

        將複雜的布爾表達式轉換爲命名精確的布爾函數

        將條件語句中不同部分中的重複代碼合併

        使用break或return而不是循環控制變量

        在嵌套的if-then-else語句中一旦知道結果就立刻退出,而不是僅僅賦一個返回值

        用多態來代替條件語句(尤其是重複的case語句)

        創建並使用空對象代替對空值的檢測

3、子程序的重構

        提取子程序

        將子程序代碼內聯化

        將冗長的子程序轉化爲類

        用簡單的算法替代複雜的算法

        增加參數

        減少參數

        將查詢操作同修改操作區分開

        合併功能相似的子程序,並用參數來區分他們

        通過傳遞不同的參數使用子程序體現不同的功能

        傳遞整個對象而非特定成員

        傳遞特定成員而非整個對象

        封裝向下轉型操作

4、類實現的重構

        將值對象改變引用對象

        將引用對象改爲值對象

        用數據初始化來代替虛函數

        改變成員函數或數據的位置

        將特定代碼提出生成派生類

        將相似的代碼合併起來放到基類中

5、類接口的重構

        將某成員子程序放到另一個類中

        將一個類轉化爲兩個

        刪除某個關係

        隱藏委託關係

        去掉中間人

        用委託代替繼承

        用繼承代替委託

        引入外部子程序

        引入擴展類

        封裝暴露在外的成員變量

        對不能修改的成員去掉set()函數

        隱藏在類的外部不會使用的成員函數

        封裝不會用到的成員函數

        如果基類和派生類的代碼實現相似,將二者合併

 6、系統級的重構

        爲無法控制的數據創建明確的索引源

        將單向類聯繫改爲雙向類聯繫

        將雙向的類聯繫改爲單向類聯繫

        使用工廠函數而非簡單的構造函數

        用異常代替錯誤代碼,或者反其道而行之

重構策略

覈對表:安全的重構

        每一改變都是系統改變策略的一部分嗎?

        在重構之前,你保存了初始代碼了麼?

        你是否保持較小的重構步伐?

        你是否同一時間只處理一項重構?

        在重構時你是否把要做的事情一條條列了出來?

        你是否設置了一個停車場,把你在重構時所想到的任何東西記下來?

        在每次重構後你會重新測試麼?

        如果所做的修改非常複雜,或者影響到了關鍵代碼,你會重新檢查這些修改嗎?

        你是否考慮過特定重構的風險,並以此來調整你的重構方法?

        你所做的修改是提升還是降低了程序的內在質量?

        你是否避免了將重構作爲先寫後改的代名詞,或者作爲拒絕重寫拙劣代碼的託詞?

要點:

        1、修改是程序一生都要面對的事情,不僅包括最初的開發階段,還包括首次發佈之後。

        2、在修改中軟件的質量要麼改進,要麼惡化。軟件演化的首要法則就是代碼演化應當提升程序的內在質量。

        3、重構成功之關鍵在於程序員應學會關注那些標誌着代碼需要重構的衆多的警告或“代碼臭味”。

        4、重構成功的另一要素是程序員應當掌握大量特定的重構方法

        5、重構成功的最後要點在於要有安全重構的策略。一些重構方法會比其他重構方法更好。

        6、開發階段的重構是提升程序質量的最佳時機,因爲你可以立刻讓剛剛產生的改變夢想變爲現實。請珍惜這些開發階段的天賜良機。

二十五、代碼調整策略 

內容:

        性能概述

        代碼調整簡介

        蜜糖和哥斯拉(像蜜糖一樣黏,你哥拉斯一樣大的問題)

        性能測量

        反覆調整

        代碼調整方法總結

代碼調整方法總結

        如果還對代碼調整能否有助於提高某個程序的性能心存疑慮,按照以下的步驟去做吧。

        1、用設計良好的代碼來開發軟件,從而使程序易於理解和修改。

        2、如果程序性能很差

             a、保存代碼的可運行版本,這樣你才能回到“最近的已知正常狀態”;

             b、對系統進行分析測量,找出熱點;

             c、判斷性能拙劣是否源於設計、數據類型或算法上的缺陷,確定是否應該做代碼調整,如果不是,請跳回第一步;

             d、對步驟c中所確定的瓶頸代碼進行調整;

             e、每次調整後都對性能提升進行測量;

             f、如果調整沒有改進代碼的性能,就恢復到步驟a保存的代碼(通常而言,超過一半的調整嘗試都只能稍微改善性能甚至造成性能惡化)。

        3、重複步驟2。

覈對表:代碼調整策略

 程序整體性能

        你是否考慮通過修改需求來提高性能?

        你是否考慮通過修改程序的設計來提高性能?

        你是否考慮通過修改類的設計來提高性能?

        你是否考慮過減少程序同操作系統的交互從而提高性能?

        是否考慮過避免I/O操作以提高性能?

        是否考慮使用編譯型語言替代解釋型語言以提高性能?

        是否考慮過使用不同的硬件來提高性能?

        是否僅僅將代碼調整看做是解決問題的最後一招?

代碼調整方法

        在開始調整代碼之前,程序是完全正確的麼?

        在調整之前是否測量過性能瓶頸在什麼地方?

        是否記錄了每一次修改所產生的效果?

        如果沒有帶來預期的性能提高,你是否放棄了所做的代碼調整改變?

        你是否對每一個性能瓶頸進行不止一次的修改嘗試-----也就是說,你是在反覆進行代碼調整麼?

要點:

        1、性能只是軟件整體質量的一個方面,通常不是最重要的。精細的代碼調整也只是實現整體性能的一種方法,通常也不是決定性的。相對於代碼本身的效率而言,程序的架構、細節設計以及數據結構和算法選擇對程序的運行速度和資源佔用的影響通常會更大。

         2、定量測量是實現性能最優化的關鍵。定量測量需要找出能真正決定程序性能的部分,在修改之後,應當通過重複測量來明確修改是提高還是降低了軟件的性能。

        3、絕大數的程序都有那麼一小部分代碼耗費了絕大部分的運行時間。如果沒有測量,你不會知道是哪一部分代碼。

        4、代碼調整需要反覆嘗試,這樣才能獲得理想的性能提高。

        5、爲性能優化工作做好準備的最佳方式就是在最初階段編寫清晰的代碼,從而使代碼在後續工作中易於理解和修改。

二十六、代碼調整技術

內容:

        邏輯

        循環

        數據變換

        表達式

        子程序

        用低級語言重寫代碼

        變得越多,事情反而越沒變

1、邏輯

      在知道答案後停止判斷

        按照出現頻率來調整判斷順序

        用查詢表替代複雜表達式

        使用惰性求值

2、循環

       將判斷外提

        合併:就是把兩個對相同一組元素進行操作的循環合併在一起。好處就是把兩個循環的總開銷減少至單個循環的開銷。

        展開:循環展開的目的是減少維護循環所需要做的工作。

        儘可能減少在循環內部做的工作

        哨兵值:當循環的判斷條件是一個複合判斷的時候,你可以通過簡化判斷來節省代碼運行時間。如果該循環是一個查找循環,簡化方法之一就是使用一個哨兵值,你可以把它放到循環範圍的未尾,從而保證循環一定能夠中止。

        把最忙的循環放在最內層

        削減強度:削減強度意味着用多次輕量級運算(例如加法)來代替一次代價高昂的運算(例如乘法)。

3、數據變換

         數據類型的改變同樣可以成爲減少程序規模和改進執行速度方面的利器。

        使用整數而不是浮點數。

        數組維度儘可能少。

        儘可能減少數組引用。除了避免對二維或三維數組的訪問,減少對數組的訪問總是有好處的。

        使用輔助索引:使用輔助索引的意思就是添加相關數據,使得對某種數據類型的訪問更爲高效。

        使用緩存機制:緩存機制就是把某些值存起來,使得最常用的值會比不太常用的值更容易被獲取。

4、表達式

       程序裏的很多工作都是在數據或邏輯表達式中實現的。複雜的表達式往往代價昂貴。

        利用代數恆等式:例如 not a and not b = not(a or b),如果選擇第二個表達式,就避免一次not操作

        削弱運算強度:用加法代替乘法、用乘法代替冪乘、.............、用移位操作代替整數乘2或除2。

        編譯期初始化

        小心繫統函數:系統函數運行起來很慢,提供的精度常常也是根本不需要的。

        使用正確的常量類型

        預先算出結果

        刪除公共子表達式:如果發現某個表達式老是在你面前出現,就把它賦給一個變量,然後在需要的地方引用該變量,而非重新計算這個表達式。

5、子程序

      代碼調整的利器之一就是良好的子程序分解。

        將子程序重寫爲內聯

6、用低級語言重寫代碼

       有一句亙古不變的箴言也不能不提:當程序遭遇性能瓶頸的時候,你應該用低級語言重寫代碼。

覈對表:代碼調整方法

同時改善代碼執行速度和規模

        用查詢表替換複雜邏輯

        合併循環

        使用整型變量而非浮點變量

        在編譯時初始化數據

        使用正確的常量類型

        預先計算結果

        刪除公共子表達式

        將關鍵子程序代碼轉化爲某種低級語言代碼

僅僅提高代碼執行速度

        在知道答案後就停止執行判斷

        根據各種情況的出現頻率對 case 語句和 if-then-else 串排序

        比較相似邏輯結構的性能

        使用惰性求值

        將循環中的 if 判斷轉到外部

        展開循環

        將循環內部所做的工作減少到最低限度

        在查找循環中使用哨兵

        把執行最爲頻繁的循環放在嵌套循環的最裏面

        減輕內層循環的強度

        將多維數組改爲一維數組

        最大限度減少數組索引

        爲數據類型擴充索引

        對頻繁使用的值進行緩存

        利用代數恆等式

        降低邏輯和數學表達式的強度

        注意系統調用

        用內聯子程序重寫代碼

要點:

        1、優化結果在不同的語言、編譯器和環境下有很大差異。如果沒有對每一次的優化進行測量,你將無法判斷優化到底是幫助還是損害了這個程序。

        2、第一次優化通常不會是最好的。即使找到了效果很不錯的,也不要停下擴大戰果的步伐。

        3、代碼調整這一話題有點類似於核能,富有爭議,甚至會讓人衝動。一些人認爲代碼調整損害了代碼可讀性和可維護性,他們絕對會交期棄之不用。其他人則認爲只要有適應的安全保障,代碼調整對程序是有益的。如果你決定用本章所述的調整方法,請務必謹慎行事。

 第6部分  系統考慮

本章部分內容

        第27章 程序規模對構建的影響

        第28章 管理構建

        第29章  集成

        第30章 編程工具

 二十七、程度規模對構建的影響

內容:

        交流和規模

        項目規模的範圍

        項目規模對錯誤的影響

        項目規模對生產率的影響

        項目規模對開發活動的影響

1、交流和規模

        如果項目中只有你一個人,那麼唯一的交流路徑就是你和你顧客的交流,隨着項目成員數目的增加,交流路徑的數量也隨之增加,但是二者的關係並不是加性的,而是乘性的,即交流路徑的條數大致正比與人數的平方。

2、項目規模的範圍
你所從事的項目的規模具有典型嗎?項目規模的範圍很寬,這也就意味着不能把任何一種規模視爲典型。

3、項目規模對錯誤的影響

        項目的規模會影響錯誤的數量,也會影響錯誤的類型。你也許不曾想到錯誤類型也會受到影響,然而隨着項目規模的增大,通常更大一部分錯誤要歸咎於需求和設計。 

5、項目規模對開發活動的影響

        如果你從事的是一個單人項目,那麼對項目成敗影響最大的因素就是你自己。如果你在一個具有25個人的項目中工作,那麼你仍然可能是最大的影響因素,但更多可能的是,沒人能夠獨享成功的獎牌,組織結構對項目成敗的影響力更大。

        活動比例和項目規模:項目越大,所需進行的各種活動的種類也會急劇變化。

        程序、產品、系統和系統產品:代碼行數和團隊規模並不是影響項目大小的僅有因素。另一個更敏感的影響因素是最終軟件的質量和複雜度。

        方法論和規模:各種方法論都被用於不同大小的項目。對於小項目,方法論的應用顯得很不經意並且趨於本能。對於大項目,它們的應用變得十分嚴格,並且計劃得非常仔細。

要點:

        1、隨着項目規模的擴大,交流需要加以支持。大多數方法論的關鍵點都在於減少交流中的問題,而一項方法論的存亡關鍵也應取決於它能否促進交流。

        2、在其他條件都相等的時候,大項目的生產率會低於小項目。

        3、在其他每件都相等的時候,大項目的每千行代碼錯誤率會高於小項目

        4、在小項目裏的一些看起來“理當如此”的活動在大項目中必須仔細地計劃。隨着項目規模擴大,構建活動的主導地位逐漸降低。

        5、放大輕量級的方法論要好於縮小重量級的方法論,最有效的辦法是使用“適量級”方法論。

二十八、管理構建

內容:

        鼓勵良好的編碼實踐

        配置管理

        評估構建進度表

        度量

        把程序員當人看

        管理你的管理者

1、鼓勵良好的編碼實踐

        設定標準的考慮事項

        鼓勵良好的編碼實踐的技術

2、配置管理

        什麼是配置管理:配置管理是“系統化地定義項目工件和處理變化,以使項目一直保持其完整性”的實踐活動。它的另一種說法是“變量控件”。其中的技術包括評估所提交的更改、追蹤更改、保留系統在不同時間點的各歷史版本。

        需求變易和設計變更

        軟件代碼變更

        工具版本

        機器配置

        備份計劃

覈對表:配置管理

概要

        你的軟件配置管理計劃是否用於幫助程序員,並能將額外負擔降至最低。

        你的軟件配置管理方法是否避免了對項目的過度控制?

        你是否將一些變更請求聚成一組?無論採用非正式的方法(如創建一份未決更改的列表)還是更加系統的方法(如設立變更控制委員會)。

        你係統地評估了每一項提交的更改對成本、計劃和質量的影響嗎?

        你是否把重大的變更看做是需求分析還不夠完備的警報信號嗎?

工具

        你用版本控件軟件來促進配置管理嗎?

        你用版本控件軟件來減少團隊工作的協調問題嗎?

備份

        你定期地備份項目中的所有資料嗎?

        你定期地把備份項目備份數據轉移到off-site storage 裏了嗎?

        所有的資料,包括源代碼、文檔、圖表和重要的筆記都得到備份了嗎?

        你測試過備份與恢復的過程嗎?

3、評估構建進度表

        評估的方法

        評估構建的工作量

        對進度的影響

        評估與控制

4、度量

        度量軟件項目有很多種方法。以下是對項目進行度量的兩項根本原因

        任何一種項目特徵(attribute)都是可以用某種方法來度量的,而且總會比根本不度量好得多。

        留心度量的副作用

        反對度量就是認爲最好不要去了解項目中到底在發生什麼

5、把程序員當人看

        程序員們怎樣花費時間

        性能差異與質量差異(個體差異、團隊差異)

        信仰問題(編程語言、縮進風格、大括號的擺放位置、所用的集成開發環境、註釋風格、效率與可讀性的取捨、對方法的選擇--例如,Scrum-極限編程-漸進雲交付、編程工具、命名習慣、對goto的使用、對全局變量的使用、量度---特別是有關生產力的量度,如每天編寫的代碼行數)

        物理環境

6、管理你的管理者

        在軟件開發中,非技術出身的管理者隨處可見,具有技術經驗但卻落後於這個時代10年(以上)的管理者也比比皆是。技術出色並且其技術與時俱進的管理者實屬鳳毛麟角。如果你正在爲一位這樣的管理者工作,那麼就儘可能地保住你的工作吧。這可是非常難得的待遇。

要點:

        1、好的編碼實踐可以通過“貫徹標註”或者“使用更爲靈活的方法”來達到。

        2、配置管理,如果應用得當,會使程序員的工作變得更加輕鬆。特別包括變量控制。

        3、好的軟件評估是一項重大挑戰。成功的關鍵包括採用多種方法、隨着項目的開展而修繕評估結果,以及很好地利用數據來創建評估等。

        4、度量是構建管理成功的關鍵。你可以採取措施度量項目的任何方面,而這要比根本不度量好得多。準確的度量是制定準確的進度表、質量控制和改進開發過程的關鍵。

        5、程序員和管理人員都是人,在把他們當人看的時候工作得最好。

二十九、集成

內容:

        集成方式的重要性

        集成頻率---階段式集成還是增量集成

        增量集成的策略

        Daily Build(每日構建)與冒煙測試

1、集成方式的重要性

        在軟件以外的工程領域,正確集成的重要性已廣爲人知。一起事故戲劇性地展示了糟糕的集成的危險,華盛頓大學的露天足球場在建設時中途坍塌。因爲它在建造時不能支撐自己的重量。很可能在完工後它會足夠牢固,但是它在建造順序是錯誤的------這是一個“集成”錯誤。它必須在每一步都要足夠牢固才行。如果你按錯誤的順序構建並集成軟件,那麼會難於編碼、難於測試、難於調試。

2、 集成頻率---階段式集成還是增量集成

        階段式集成

        增量集成

        增量集成的益處:易於定位錯誤、及早在項目裏取得系統級的成果、改善對進度的監控、改善客戶關係、更加充分地測試系統中的各個單元、能在更短的開發進度計劃內建造出整個系統。

3、增量集成的策略自頂向下集成、自底向上集成、三明治集成、風險導向的集成、功能導向的集成、T-型集成。

        集成方法的小結:請不要像教條一樣遵循前面提到的任務過程,而應該爲特定項目剪裁一套獨一無二的策略。

4、Daily Build(每日構建)與冒煙測試

        無論你選用哪種集成策略,dayil build和冒煙測試都是軟件集成的好方法。每天都將各個(源)文件編譯、鏈接並組合爲一個可執行程序,然後對這個程序進行冒煙測試,即執行一種相對簡單的檢查,看看產品在運行時是否“冒煙”。

        每日構建:每日構建最關鍵的部分是"daily/每天"。

        檢查失敗的build

        每天進行冒煙測試

        讓冒煙測試與時俱進

        將daily build 和冒煙測試自動化

        成立build小組

        僅有當意義時,纔將修訂(revisions)加入build中

        ......但是加等太久纔將修訂加入進來

        要求開發人員在把他的代碼添加到系統之前,進行冒煙測試

        爲即將添加到build的代碼準備一塊暫存區

        懲罰破壞build的人

        在早上發佈build

        即使有壓力,也要進行daily build和冒煙測試

        哪些項目能用daily build 過程:持續集成

覈對表:集成

集成策略

        該策略是否指明瞭集成子系統、類、子程序時應該採用的最優順序?

        集成的順序是否與構建順序協調,以便在適當的時候準備好供集成的類?

        該策略是否易於診斷缺陷?

        該策略是否使腳手架最少?

        所選的策略是否好於其他方式?

        組件之間的接口是否有明確定義?(定義接口不是集成的任務,但要驗證這些接口的定義是否正確。)

Daily build與冒煙測試

        項目是否經常build------理想情況下,每天build一次------以支持增量集成?

        每次build後是否都運行冒煙測試,讓你知道這個build能否工作?

        你是否已使build和冒煙測試自動進行?

        開發人員是否頻繁地check in他們的代碼------兩次check in之間最多間隔一兩天?

        冒煙測試是否與代碼同步更新,隨代碼發展而發展?

        破壞build是罕見事件嗎?

        是否在有壓力的情況下,也對軟件進行build和冒煙測試?

要點:

        1、構建的先後次序和集成的步驟會影響設計、編碼、測試各類的順序。

        2、一個經過充分思考的集成順序能減少測試的工作量,並使調試更容易。

        3、增量集成有若干變型,而且------除非項目是微不足道的------任何一種形式的增量集成都比階段式集成好。

      4、針對每個特定的項目,最佳的集成步驟通常是自頂向下、自底向上、風險導向及其他集成方法的某種組合。T-集成和豎直分塊集成通常都能工作得很好。

      5、daily build能減少集成的問題,提升開發人員的士氣,並提供非常有用的項目管理信息。

三十、編程工具

內容:

        設計工具

        源代碼工具

        可執行碼工具

        工具導向的環境

        打造你自己的編程工具

        工具幻境

現代化的編程工具減少了構建所需的時間。使用最前沿的工具集---並熟悉你所用的工具---能使用生產力增加50%還不止。編程工具也能降低編程中必須的單調乏味的瑣碎事務的勞動量。

        工具幻境當你聽到某個工具廠商宣稱“這一新工具將會消除計算機程序設計時”,躲開它!或者對這種廠商的幼稚的樂觀主義一笑置之。

覈對表:編程工具

        你有一套有效的IDE嗎?

        你的IDE集成了:源代碼控制、build/測試/除錯工具,以及其他有用的功能嗎?

        你有能自動進行常用的重構操作的工具嗎?

        你是否使用版本控制工具,對源代碼、內容、需求、設計、項目計劃及其他的項目構件進行管理?

        如果你正面對超大型的項目,你是否使用了數據字典或者其他“包含系統中使用的各個類的權威描述”的中央知識庫。

        當可以用到代碼庫是時,你是否考慮用它來代替“編寫定製代碼”?

        你是否充分利用了交互式除錯器?

        你是否使用make或者他“依賴關係控件軟件”,用來高效並可靠地build程序?

        你的測試環境包含有自動化的測試框架、自動測試生成器、覆蓋率監視器、系統擾動器、diff工具,以及缺陷跟蹤軟件嗎?

       你有沒有製造過定製工具-----能滿足特定項目的需求的那種,特別是能自動執行重複任務的工具?

        總而言之,你的工作環境有沒有從“充足的工具支援”中獲益?

要點:

        1、程序員有時會在長達數年的時間裏忽視某些最強大的工具,之後才發現並使用之。

        2、好的工具能讓你的日子過得安逸很多。

        3、下面這些工具已經可用了;編輯、分析代碼質量、重構、版本控制、除錯、測試、代碼調整。

        4、你能打造許多自己用的專用工具。

        5、好的工具能減少軟件開發中最單調乏味的工作的量,但它不能消除對“編程”的需要,雖然它會持續地重塑(reshape)“編程的含義”。

第7部分    軟件工藝

本部分內容

        第31章     佈局與風格

        第32章     自說明代碼

        第33章    個人性格

        第34章     軟件工藝的話題

        第35章     何處有更多信息

三十一、佈局與風格

內容:

        基本原則

        佈局技術

        控制結構的佈局

        單條語句的佈局

        註釋的佈局

        子程序的佈局

        類的佈局

本章轉向計算機編程的美學問題---程序源代碼的佈局。編排出色的代碼會帶來視覺上和思維上的愉悅,這是非程序員的人不能感受到時的。而精雕細琢代碼、使之達到美觀的程序員們,卻會從這一過程得到藝術上的滿足。

覈對表:佈局

一般問題

        格式化主要是爲了展現代碼的邏輯結構嗎?

        你的佈局方案能統一地運用嗎?

        你的佈局方案能讓代碼易於維護嗎?

        你的佈局方案是否有利於代碼的可讀性?

控件結構的佈局

        你的代碼中避免begin-end對或 { } 的又重縮進了嗎?

        相鄰的塊之間用空行分隔了嗎?

        對複雜表達式格式化時考慮到可讀性嗎?

        對只有一條語句的塊的佈局始終如一嗎?

        case語句與其他控制結構的格式化保持一到了嗎?

        對goto語句的格式化是否讓其顯眼了呢?

單條語句的佈局

        爲邏輯表達式、數組下標和子程序參數的可讀性而使用空格了嗎?

        不完整的語句在行末是以明顯有錯的方式結束了嗎?

        後續行按照標準數目縮進了嗎?

        每行頂多只有一條語句嗎?

        所寫的每個語句都沒有副作用嗎?

        每行頂多只聲明一個數據嗎?

註釋的佈局

        註釋與其所註釋的代碼的縮進量相同嗎?

        註釋的風格使用維護嗎?

子程序的佈局

        你對每個子程序參數的格式化方式便於看懂、修改、註釋嗎?

        採用空行分隔子程序的各部分了嗎?

 類、文件和程序的佈局

        多數類和文件之間是一一對就的關係嗎?

        如果文件內有多個類,各類中的子程序按類分組了嗎?各類都清楚標識了嗎?

        文件中的子程序用空行清楚地分開了嗎?

        在沒有更好的組織形式的場合,所有子程序都按字母順序排列了嗎?

要點:

        1、可視化佈局的首要任務是指明代碼的邏輯組織。評估該任務是否實現的指標包括準確性、一致性、易讀性和易維護性。

        2、外表悅目比起其他指標是最不重要的。然而,如果其他指標都達到了,代碼又質量好,那麼佈局效果看上去也會不錯。

        3、Visual Basic具有純代碼風格,而java的傳統做法就是使用純塊風格,所以若用這些語言編程,就請使用純代碼風格。C++中,模擬純代碼塊或者 begin-end 塊邊界都行之有效。

        4、結構化代碼有其自身目的。始終如一地沒用某個習慣而少來創新。不能持久的佈局規範只會損害可讀性。

        5、佈局的很多方面涉及信仰問題,應試着將客觀需要和主觀偏好區分開來。定出明確的目標 ,在些基礎了再討論風格參數的選擇。

三十二、自說明代碼

內容:

        外部文檔

        編程風格作文檔

        註釋或不註釋

        高效註釋之關鍵

        註釋技術

        IEEE標準(電子與電器工程師協會)

覈對表:自說明代碼

        你的類接口體現出某種致的抽象嗎?

        你的類名有意義嗎,能表明其中心意圖嗎?

        你的類接口對於如何使用該類顯而易見嗎?

        你的類接口能抽象到不需要考慮其實現過程嗎?能把類看成是黑盒嗎?

 子程序

        你的每個子程序名都能準確地指示該子程序確幹些什麼嗎?

        你的各子程序的任務明確嗎?

        若各子程序中自成一體後更有用,你都將其各自獨立出來嗎?

        每個子程序的接口都清晰明瞭嗎?

數據名

        類型名描述有助於說明數據聲明嗎?

        你的變量名有意義嗎?

        變量只用在其名字所代表意義的場合嗎?

        你的循環變量名能給出更多信息,而不是 i、j、k 之類的嗎?

        你用了名字有意義的枚舉類型,而非臨時拼湊的標識或者布爾變量嗎?

        用具名常量代替神祕數值或者字符串了嗎?

        你的命名規範能區分類型名、枚舉類型、具名常量、局部變量、類變量以及全局變量嗎?

 數據組織

        你根據編程清晰的需要,使用了額外變量來提高清晰度嗎?

        你對某變量的引用集中嗎?

        數據類型簡化到了最低複雜度嗎?

        你是通過抽象訪問子程序(抽象數據類型)來訪問複雜數據嗎?

控制

        代碼中的正常執行路徑很清晰嗎?

        相關的語句放在一起了嗎?

        相對獨立的語句組打包成子程序了嗎?

        正常情況的處理位於 if 語句之後,而非在 else 子句中嗎?

        控制結構簡單明瞭,以使複雜度最低嗎?

        每個循環完成且僅完成一個功能,是像定義良好的子程序那麼做嗎?

        嵌套層次是最少嗎?

        邏輯表達式通過額外添加布爾變量、布爾函數和功能表簡化了嗎?

佈局

        程序的佈局能表現出其邏輯結構嗎?

設計

        代碼直接了當嗎?是不是避免自作聰明或新花樣?

        實現細節儘可能隱藏了嗎?

        程序是儘可能採用問題領域的術語,而非按照計算機科學或者編程語言的術語編寫的嗎?

 5、註釋技術

        註釋單行

        註釋代碼段

        註釋數據聲明

        註釋子程序

        註釋類、文件和程序:

                類、文件和程序的共同特徵是它們都包含多個子程序。文件或類應包含彼此相關的一組子程序;程序包含了所有的子程序。各情況下注釋的任務就是要對文件、類或程序內容提供有意義的概括說明。

                程序註釋以書本爲範例:

                        序”:爲一組常於文件頭的註釋,起到介紹程序的作用。它和書的序功能相同,提供有關程序的情況。

                        “目錄”:給出頂層文件、類和子程序(即“章”),它們可以是清單形式,就如同書本列出章節那樣,也可以結構圖的形式表示。

                        “節”:則是子程序內的各單位,例如子程序聲明、數據聲明和可執行語句。

                        “交叉引用”:是代碼的“參閱......”映射,其中包含行號。

6、IEEE(電子與電器工程師協會)標準

        軟件開發標準

        軟件質量保標準

        管理標準

        標準綜述

覈對表:好的註釋技術

一般問題

        別人拿起你的代碼就能立刻明白其意嗎?

        你的註釋是在解釋代碼用意,或概括代碼在做什麼,而非簡單重複代碼嗎?

        採用了僞代碼編程法減少註釋時間嗎?

        是重寫有玄機的代碼,而非爲其做註釋嗎?

        你的註釋是否能同代碼一起更新?

        註釋清楚正確嗎?

        你的註釋風格便於修改註釋嗎?

語句和段落

        代碼避免用行尾註釋了嗎?

        註釋是着力說明爲什麼而非怎麼樣嗎?

        註釋爲將要閱讀代碼的人們做好準備了嗎?

        每個註釋都其用處嗎?刪掉抑或改進了多餘的、無關緊要的或隨意的註釋沒有?

        是否註釋了代碼的非常規之處?

        避免使用縮略語了嗎?

        主次註釋區別明顯嗎?

        含錯代碼和未公開的代碼特性有註釋嗎?

數據聲明

        對數據聲明的註釋說明了數值單位嗎?

        數值數據的取值範圍註釋出來了嗎?

        註釋出的編碼含義嗎?

        對輸入數據的限制有註釋嗎?

        對位標誌做註釋了嗎?

        在各全局變量聲明的地方對其做註釋了嗎?

        各全局變量是通過命名規範、註釋(或兩者兼用)來標識其意義嗎?

        神祕數值是否以具名常量代替,而非只是標註之?

控制結構

        控制語句都註釋了嗎?

        冗長或者複雜的控制結構結尾處有註釋嗎?抑或可能的話,簡化之從而省去註釋了嗎?

子程序

        各子程序的意圖都註釋出了嗎?

        子程序的其他有關情況(諸如輸入輸出數據、接口假設、侷限性、糾錯、全局效果和算法來源)都註釋出來了嗎?

文件、類和子程序

        程序有簡短的文檔(就像在“以書本爲範例”中說明的那樣)給出程序組織的概述嗎?

        每個文件的用途都有說明嗎?

        作者姓名、email及電話號碼在代碼清單中都有嗎?

要點:

        1、該不該註釋是個需要認真對待的問題。差勁的註釋只會浪費時間,幫倒忙;好的註釋才有價值。

        2、源代碼應當含有程序大部分的關鍵信息。只要程序依然在用,源代碼比其他資料都能保持更新,故而將重要信息融入代碼是很有用處的。

        3、好代碼本身就是最好的說明。如果代碼太糟,需要大量註釋,應先試着改進代碼,真至無須過多註釋爲止。

        4、有的註釋風格需要許多重複性勞動,應捨棄之,改用易於維護的註釋風格。

三十三、個人性格

內容:

        本人性格是否和本書話題無關

        聰明和謙虛

        求知慾

        誠實

        交流與合作

        創造力和紀律

        懶惰

        不如你相像中那樣起作用的性格因素

        習慣

         每個行業的工程人員都瞭解其工作用的工具及材料和各種限制。如果是電氣工程師,就該知道各種金屬的電導率,以及電壓表的上百種用法;如果是建築工程師,就該知道木材、混凝土和鋼鐵的承載量。

        假如你是軟件工程師,基本的建造材料就是你的聰明才智,主要工具就是你自己。建築工程師對建築物結構進行詳細的設計,然後將設計藍圖交給其他人施工;而你一旦從細節上對軟件做好設計後,軟件就生成出來了。編程的整個過程如同建造空中樓閣一樣——這是人們能做的純粹腦力勞動之一。

        因此,當軟件工程師研究其工具和原材料的本質時,會發現其實是在研究人的智力、性格,這些可不像木材、混凝土和鋼材是有形的東西。

3、求知慾

        在開發過程中建立自我意識

        試驗

        閱讀解決問題的有關方法

        在行動之前做分析和計劃

        學習成功項目的開發經驗

        閱讀文檔

        閱讀其他書本期刊 

        同專業人士交往

        向專業開發看齊——第一級入門級、第二級中級、第三級熟練級、第四級技術帶頭人級(具有第三級的專業才學),並明白編程工作中只有15%用來和計算機交互、其餘都是與人打交道的。

4、誠實

        不是高手不假裝是高手

        樂於承認錯誤

        力圖理解編譯器的警告,而非棄之不理

        透徹理解自己的程序,而不要只是編譯看看能否運行

        提供實際的狀況報告

        提供現實的進度方案,在上司面前堅持自己的意見

9、習慣

       我們的精神品德即非與生俱來,也非逆於天性......其發展歸因於習慣......我們要學的任何東西都通過實際做的過程學到......如果人們建的房子好,他們就成爲好的建設者;而造的房子不好時,他們就是差的建設者......所以小時候形成了怎樣的習慣關係很大——正是它會造成天壤之別,或者說就是世上所有差異之源。

要點:

        1、人的個性對其編程能力有直接影響

        2、最有關係的性格爲:謙虛、求知慾、誠實、創建性和紀律,以及高明的偷懶。

        3、程序員高手的性格與天分無關,而任何事都與個人發展相關。

        4、出乎意料的是,小聰明、經驗、堅持和瘋狂既有助也有害。

        5、很多程序員不願主動吸收新知識和技術,只依靠工作時偶爾接觸新的信息。如果你能抽出少量時間閱讀和學習編程知識,要不了多久就能鶴立雞羣。

        6、好性格與培養正確的習慣關係甚大。要成爲傑出的程序員,先要養成良好習慣,其他自然水到渠成。

三十四、軟件工藝的話題

內容:

        征服複雜性

        精選開發過程

        首先爲人寫程序,其次纔是爲機器

        深入一門語言去編程

       藉助規範集中注意力

        基於問題域編程

        當心落石

        迭代、反反覆覆,一次以一次

        汝當分離軟件與信仰

1、征服複雜性:致力於降低複雜度軟件開發的核心。第5章“軟件構建中的設計”說明了管理複雜度是軟件的首要技術使命。

       例:A、在架構層將系統劃分爲多個子系統,便讓思緒在某段時間內能專注於系統的一小部分

              B、仔細定義類接口,從而可以忽略類內部的工作機理

              C、保持類接口的抽象性、從而不必記住不必要的細節

              D、避免全局變量,因爲它會大大增加總是需要兼顧的代碼比例

              E、避免深層次的繼承,因爲這樣會耗費很大精力

              F、避免深度嵌套的循環或條件判斷,因爲它們都能用簡單的控制結構取代,後者佔用較少的大腦資源。

              G、別用goto,因爲它們引入非順序執行,多數人都不容易弄懂。

              H、小心定義錯誤處理的方法,不要濫用不同的錯誤處理技術。

              I、以系統的觀點對待內置的異常機制,後者會成爲非線性的控制結構。異常如果不受約束地使用,會和goto一樣難以理解。

              J、不要讓類過度膨脹,以致於佔據整個程序。

              K、子程序應保持短小

              L、使用清楚、不言自明的變量名,從而大腦不必費力記住諸如“ i 代表賬號下標,j 代表顧客下標,還是另有它意?”之類的細節。   

              M、傳遞給子程序的參數數目應儘量少。更重要的是,只傳遞保持子程序接口抽象所必需的參數。

              N、用規範和約定來使大腦從記憶不同代碼的隨意性、偶然性差異中解脫出來。

              O、只要有可能,一般情況下應避免第5章所說的“偶然性困難”。

 3、首先爲人寫程序,其次纔是爲機器

        你的程序(名詞)好似迷宮一樣令人摸不清方向,夾雜着各種花招和無關注釋。對比:我的程序

        我的程序(名詞)算法精準,結構緊湊,性能好,註釋清晰得體。對比:你的程序

4、深入一門語言去編程,不浮於表面

        不要將編程思路侷限到所用語言能自動支持的範圍。傑出的程序員會考慮他們要幹什麼,然後纔是怎樣用手頭的工具去實現他們的目標。

6、基於問題域編程

        另一個處理複雜性的特殊方式就是儘可能工作於最高的抽象層次。其中一種方法就是針對編程所要解決的問題來工作,而非針對計算機科學的解決方案。

        將程序劃分爲不同層次的抽象

                好的設計使你可以把很多時間集中在較高層,而忽略較低層的細節

                第0層:操作系統的操作和機器指令

                第1層:編程語言結構和工具

                第2層:低層實現結構

                第3層:低層問題域

                第4層:高層問題域

        問題域的低層技術

                1、在問題域使用類,來實現有實際意義的結構

                2、隱藏低層數據類型及其實現細節的信息

                3、使用具名常量來說明字符串和文字量的意義

                4、對中間計算結果使用中間變量

                5、用布爾函數使複雜邏輯判斷更清晰

7、當心落石

        編程中的警告信息能提醒程序中可能的錯誤,但它們一般不會像路標“當心落石”那樣顯眼。

要點:

        1、編程的主要目的之一是管理複雜性。

        2、編程過程對最終產品有深遠影響。

        3、合作一發要求團隊成員之間進行廣泛溝通,甚於同計算機的交互; 而單人開發則是自我交流,其次纔是與計算機。

        4、編程規範一旦濫用,只會雪上加霜;使用得當則能爲開發環境帶來良好機制,有助於管理複雜性和相互溝通。

        5、編程應基於問題域而非解決方案,這樣便於複雜性管理。

        6、注意警告信息,將其作爲編程的疑點,因爲編程幾乎是純粹的智力活動。

        7、開發時迭代次數越多,產品的質量越好。

        8、墨守成規的方法有悖於高質量的軟件開發。請將編程工具箱中填滿各種編程工具,不斷提高自己挑選合適工具的能力。

三十五、何處有更多信息

內容:

        關於軟件構建的信息

        構建之外的話題

        期刊

        軟件開發者的閱讀計劃

        參加專業組織

    讀到這兒時,你應已對行這有效的軟件開發實踐有了不少的瞭解。實際上你還能獲取比這些多得多的信息。你犯的錯誤別人早已犯過,要是不想自討苦吃,就讀讀他們的書吧,這樣能夠避免再走彎路,並找出解決問題的新方法。

3、期刊

        初階程序員技術雜誌

        高階程序員學術期刊

        專題出版物:專業出版物、通俗出版物、

4、軟件開發者的閱讀計劃

       入門級

       熟練級

        專業級

5、參加專業組織

       瞭解編程的最好途徑是與其他程序員交往。最好是面向實踐的組織是IEEE計算機社區。請訪問www.computer.org

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章