去年寫的代碼大全筆記(其實是摘記)

Reader: Roof

前言:

    雖然近年來前衛的開發實踐迅速發展,但普通的實踐手段並沒有太大變化。很多程序的開發依然漏洞百出、遲於交付並且超出預算。
    軟件業的現狀是,很多程序員並未接受正規的軟件開發教育。
    軟件業的內行人士,幾乎沒有心情去分享他們的編程經驗和技術,即使有,也是非常零散的。
    學習並掌握不止一門語言通常是專業程序員職業生涯的分水嶺。
    軟件構建這個階段,常常被忽視。
    “代碼構建”一般佔據了小型項目的65%的工作量,而在中型項目上也達到了50%,同時,“構建”也要爲小型項目中的75%的錯誤負責,在中到大型項目上這一比例也達到了50% 。而任何要爲50%到75%的錯誤負責的活動環節,顯然是應該加以改善的。
    軟件中一些代價最爲昂貴的錯誤,其罪魁禍首常常是一些小範圍的代碼錯誤,其代價甚至可以飆至上億美元的程度。

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

    (軟件構建中)在太不正規和太正規之間找一個平衡點是不容易的。
    構建活動主要關注於編碼與調試,但也包含詳細設計、單元測試、集成測試以及其他一些活動。
    構建有時也被認爲是“編碼(coding)”或“編程(programming)”。“編碼”算不上是最貼切的詞,因爲它有一種“把已經存在的設計機械化地翻譯成計算機語言”的意味;而構建不是那麼機械化的,需要可觀的創造力和判斷力。
    構建活動的產物---源代碼---往往是對軟件的唯一精確描述。
    現實中不那麼完美的軟件項目,往往跳過需求和設計的階段而直接躍入構建環節,之後又由於有太多的錯誤要修正而時間又不夠,測試環節也被拋到一邊了。但是,無論一個項目的計劃有多匆忙、多糟糕,它都不可能扔下構建活動---這是不可或缺的環節。
    在構建活動期間,不同程序員的生產率的差異可達10到20倍。

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

    重要的研發成果常常產自類比。通過把你不太理解的東西和一些你較爲理解、且十分類似的東西做比較。
    有時候,當隱喻的概念被過度引申時,模型也會誤導人們。
    科學發展的歷史並不是一系列從“錯誤”的隱喻到“正確”的隱喻的轉變,而是一系列從“不太合適”的隱喻到“更好”的隱喻的轉變。
    對於編程來說,最大的挑戰還是將問題概念化,編程中的很多錯誤都是概念性的錯誤。
    Capers Jones發表的報告稱,一套100萬行的代碼的軟件系統,平均需要69種文檔(1998)。其需求規格文檔一般有四五千頁長,而設計文檔常常是需求的兩三倍長。

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

    目前,軟件開發中最常見的項目風險是糟糕的需求分析和糟糕的項目計劃,因此準備工作就傾向於集中改進需求分析和項目規劃。
    你可能會認爲,所有的專業程序員都知道準備工作的重要性,並且在躍入構建活動之前檢查確認所有先決條件都已經滿足了。很不幸,這不是事實。
    在實現一個系統之前,你需要理解“這個系統應該做什麼”,以及“它該如何做到這些”。
    發現錯誤的時間要儘可能接近引入該錯誤的時間。缺陷在軟件食物鏈裏面呆的時間越長,它對食物鏈的後繼造成損壞就越嚴重。
    我們已經非常詳細地研究了需求和設計,我想不出在編碼和調試期間還會遇到什麼大問題。
    忽略前期準備的迭代式開發法,最終明顯會比“密切關注前期準備工作的序列式開發法”付出更高的代價。
    預先詳細說明100%的需求和設計是不切實際的,不過對絕大多數項目來說,“儘早把哪些是最關鍵的需求要素和架構要素確定下來”是很有價值的。
    問題定義應該由客戶的語言來書寫,而且應該從客戶的角度來描述問題。通常不應該用計算機的專業術語敘述。最好的解決方案未必是一個計算機程序。……這條規則也有例外,那就是需要解決的就是計算機本身相關的問題。
    如果沒有一個良好的問題的定義,你努力解決的可能是一個錯誤的問題。
    “進度”和“成本”這兩個字眼比咖啡和洗冷水都要提神,許多“必須要有/must haves”很快會變成“有就最好/nice to haves”。
    在提到實施這個項目的商業理由的時候,許多需求事項就會從你眼前消失。有些需求作爲功能特色來看是不錯的想法,但是當你評估“增加商業價值”時就會覺得它是個糟透了的主意。那些記得“考慮自己的決定所帶來的商業影響”的程序員的身價與黃金相當。
    你對全部需求感到很舒服嗎?你是否已經去掉了那些不可能實現的需求---那些只是爲了安撫客戶和老闆的東西?
    有一份對設計實踐的綜述發現,“維護‘設計的緣由’ ”至少與“維護設計本身”一樣重要。
    瞄準80/20法則:對那些構成系統80%行爲的20%的類進行詳細說明。
    數據通常只應該由一個子系統或一個類直接訪問;例外的情況就是透過訪問器類或訪問器子程序---以受控且抽象的方式---來訪問數據。
    架構應該使我們很容易地做到:砍掉交互式界面的類,插入一組命令行的類。
    架構應該描述一份管理稀缺資源的計劃。稀缺資源包括數據庫連接、線程、句柄等。
    錯誤處理已經被證實爲現代計算機科學中最棘手的問題之一,你不能武斷地處理它。
    有人估計程序中高達90%的代碼是用來處理異常情況、進行錯誤處理、或做簿記(housekeeping)工作,意味着只有10%的代碼是用來處理常規的情況。
    因爲錯誤處理牽連到整個系統,因此最好在架構層次上對待它。
    每個類在驗證其數據的有效性方面需要負何種責任?是每個類負責驗證自己的數據有效性,還是有一組類負責驗證整個系統的數據的有效性?某個層次上的類是否能假設它接收的數據是乾淨的?
    在軟件中,鏈條的強度不是取決於最薄弱的一環,而是等於所有薄弱環節的乘積。
    通常在架構中明確地設立期望目標,就能避免出現“某些類異常健壯,而其他類勉強夠健壯”的現象。
    架構應該描述所有主要決策的動機。謹防“我們向來這麼做”這種自認爲有理的說法。
    最後,你不應該擔憂架構的任何部分。架構不應該包含任何僅僅爲了取悅老闆的東西。它不應該包含任何對你而言很難理解的東西。你就是那個實現架構的人;如果你自己都弄不懂,那怎麼實現它?
    

第4章:關鍵的“構建”策略

    一套好的符號系統能把大腦從所有非必要的工作中解脫出來,集中精力去對付更高級的問題,從功效上看,能夠有效的提高人類的智力。
    研究表明,編程語言的選擇從多個方面影響生產率和代碼質量。
    Sapir-Whorf假說是,你思考的能力取決於你是否知道能夠表達該思想的詞彙。
    成功編程的一個關鍵就在於避免隨意的變化,這樣你的大腦可以專注於那些真正需要的變化。
    “你如何面對自己的編程工作”,取決於你在技術浪潮中所處的位置。
    David Gries所言,編程工具不應該決定你的編程思路。
    大多數重要的編程原則並不依賴於特定的語言,而依賴於你使用語言的方式。如果你使用的語言缺乏你希望用的構建,或者傾向於出現其他種類的問題,那就應該試着去彌補它。發明你自己的編碼約定、標準、類庫以及其他改進措施。
    在開始編程之前,做好一些約定(convention)。“改變代碼使之符合這些約定”是近乎不可能的。
    

第5章:軟件構建中的設計

    根據Horst Rittel和Melvin Webber的定義,“險惡的(wicked)”問題就是那種只有通過解決或者部分解決才能被明確的問題。這個看似矛盾的定義其實是在暗示說,你必須首先把這個問題“解決”一遍以便能夠明確定義它,然後再次解決該問題,從而形成可行的方案。這一過程已經如影隨形地在軟件開發中存在數十年了(Peters and Tripp 1976)。
    軟件設計的成果應該是組織良好、乾淨利落的,然而形成這個設計的過程卻並非如此清爽。
    從本質上說軟件開發就是不斷地去發掘錯綜複雜、相互連接的整套概念的所有細節。
    當項目確由技術因素導致失敗時,其原因通常就是失控的複雜度。
    我們不應該試着在同一時間把整個程序都塞進自己的大腦,而應該試着以某種方式去組織程序,以便能夠在一個時刻可以專注於一個特定的部分。
    一旦你能理解軟件開發中任何其他技術目標都不如管理複雜度重要時,衆多設計上的考慮就都變得直截了當了。
    (關於層次性)假設你正在編寫一個新系統,其中用到很多設計不佳的舊代碼,這時你就應該爲新系統編寫一個負責同舊代碼交互的層。在設計這一層時,要讓它能隱藏舊代碼的低劣質量,同時爲新的層次提供一組一致的服務。
    應該通過限制子系統之間的通信來讓每個子系統更有存在意義。
    信息隱藏是軟件的首要技術使命中格外重要的一種啓發式方法,因爲它強調的就是隱藏複雜度,這一點無論是從它的名稱還是其實施細節上都能看得很清楚。
    設計類的接口與設計其他環節一樣,都是一個迭代的過程。
    在少數情況下,信息隱藏是根本不可能的。不過大多數讓信息無法隱藏的障礙都是由於慣用某種技術而導致的心理障礙。
    一種更爲隱晦的信息隱藏障礙則是循環依賴。
    信息隱藏的最後一個障礙是試圖在系統架構層和編碼層均避免性能上的損耗。
    請養成問“我該隱藏些什麼?”的習慣。
    業務規則很容易成爲軟件頻繁變化的根源。
    狀態變量用於表示程序的狀態,與大多數其他的數據相比,這種東西更容易改變。
    找出容易發生變化的區域的一個好方法是:首先找出程序中可能對用戶有用的最小子集。這一子集構成了系統的核心,不容易發生改變。接下來,用微小的步伐擴充這個系統。
    應用模式的一個潛在的陷阱是強迫讓代碼適用於某個模式。
    二分法查找算法是很優雅,可往往一個蠻力的、順序的查找算法就足夠了。
    有時候你會從開發某個程序中學到很多的知識,多得讓你想帶着寫第一遍時所獲得的體會再寫一遍。
    自上而下設計通常比較容易上手,但是有時候會受底層複雜度的影響,這種影響甚至有時會使事情變得比實際的情況更復雜。自下而上設計開始起來比較複雜,但是在早期鑑別出系統的複雜度,卻有助於設計出更好的高層類。當然這樣做的前提是複雜度沒有先把整個系統破壞掉!
    對於實施正式編碼階段前的設計工作量和設計文檔的正規程度,很難有個確定的定論。有很多因素,如團隊的經驗、系統的預定壽命、想要得到的可靠度、項目的規模和團隊的大小等等都需要考慮進去。
    有一些人鼓吹軟件是一項遵守紀律的活動,他們花了相當多的精力來讓我們感到愧疚。我們耗盡畢生精力也到不了“足夠結構化”和“足夠面向對象”的極樂世界。我們都揹負着“在可塑性很強的年紀學過Basic”的原罪。但是,我敢打賭我們中的大多數人都是比那些純化論者更優秀的設計師,儘管他們不願意承認這一點。---P.J.Plauger
    請把設計看成是一個險惡的、雜亂的和啓發式的過程。
    問題求解並不是一個確定性的活動,如果你固執在某一種方法之上,那麼無異於作繭自縛。

第6章:可以工作的類


    成爲高效程序員的一個關鍵就在於,當你開發程序任一部分的代碼時,都能安全地忽視程序中儘可能多的其餘部分。
    要想理解面向對象編程,首先要理解ADT。
    儘可能選擇最高的抽象層次。
    閱讀代碼的次數要比編寫代碼多得多,即使在開發的初期也是如此。因此,爲了讓編寫代碼更方便而降低代碼的可讀性是非常不經濟的。
    每當你發現自己是通過查看類的內部實現來得知該如何使用這個類的時候,你就不是在針對接口編程了,而是在透過接口針對內部實現編程了。
    研究表明,人們在做其他事情時能記住的離散項目的個數是7加減2。
    如果派生類不準備完全遵守由基類定義的同一個接口契約,繼承就不是正確的實現技術了。
    如果你只是想使用一個類的實現而不是接口,那麼就應該採用包含方式,而不該用繼承。
    人們已經發現,過深的繼承層次會顯著導致錯誤率的增長。
    爲了不確定的性能提高而增加複雜度是不妥的。
    爲現實世界中的對象建模也許不是創建類的唯一理由,但它仍是個很好的理由!
    要避免創建什麼都知道、什麼都能幹的萬能類。
    只有行爲而沒有數據的類往往不是一個真正的類。
    類的接口應該提供一致的抽象。很多問題都是由於違背該原則而引起的。

第7章:高質量的子程序

    指針操作的可讀性通常都很差,而且也很容易出錯。通過把這些操作隔離在子程序內部,你就可以把精力集中於操作的意圖本身,而不是指針操作機制的細節。
    爲了理解程序的流程,通常並沒有必要去研究那些複雜的布爾判斷的細節。應該把這些判斷放入函數中,以提高代碼的可讀性。
    我們的目標是讓每一個子程序只把一件事情做好,不再做其他事情。
    研究表明,變量名的最佳長度是9到15個字符。
    對於超過200行代碼的子程序來說,沒有哪項研究發現它能降低成本和/或降低出錯率,而且在超過200行後,你遲早會在可讀性方面遇到問題。
    由Basili和Perricone所做的一項被廣爲引用的研究發現,程序中有39%的錯誤都是屬於內部接口錯誤---也就是子程序間互相通信時所發生的錯誤。
    如果你發現自己一直需要傳遞很多參數,這就說明子程序之間的耦合太過緊密了。
    子程序的名字是它的質量的指示器。如果名字糟糕但恰如其分,那就說明這個子程序設計得很差勁。如果名字糟糕而且又不準確,那麼它就反映不出程序是幹什麼的。不管怎樣,糟糕的名字都意味着程序需要修改。

第8章:防禦式編程

    在防禦式駕駛中要建立這樣一種思維,那就是你永遠也不能確定另一位司機將要做什麼。
    斷言對於大型的複雜程序或可靠性要求極高的程序來說尤其有用。
    處理錯誤最恰當的方式要根據出現錯誤的軟件的類別而定。
    確定一種處理錯誤參數的方法,是架構層次的設計決策,需要在那裏的某個層次解決。
    檢查函數的返回值。即使你認定某個函數絕對不會出錯,也無論如何要去檢查一下。
    僅在真正例外的情況下才使用異常---換句話說,就是僅在其他編碼實踐方法無法解決的情況下才使用異常。
    如果某種的錯誤情況可以在局部處理,那就應該在局部處理掉它。不要把本來可以在局部處理掉的錯誤當成一個未被捕獲的異常拋出去。
    僅僅因爲編程語言提供了提供了異常處理機制而使用異常,是典型的“爲用而用”;這也是典型的“在一種語言上編程”而非“深入一門語言去編程”的例子。
    讓軟件的某些部分處理“不乾淨的”數據,而讓另一些部分處理“乾淨的”數據,即可讓大部分代碼無須再擔負檢查錯誤數據的職責。
    隔欄外部的程序應使用錯誤處理技術,在那裏對數據做的任何假設都是不安全的。而隔欄外部的程序裏就應使用斷言技術,因爲傳進來的數據應該已在通過隔欄時被清理過了。
    你越早引入輔助調試的代碼,它能夠提供的幫助也越大。
    要考慮好什麼地方你需要進行防禦,然後因地制宜地調整你進行防禦式編程的優先級。
    

第9章:僞代碼編程過程

    僞代碼一經寫好,你就可以按照它去生成僞碼了,同時還把僞代碼變成編程語言中的註釋。
    程序員們更喜歡使用僞代碼,因爲它簡化了用編程語言進行構建的工作,且有助於發現細節設計的不足之處。
    如果你發現由一段僞代碼發展形成的代碼量超出了預期,那麼就把這些代碼重構爲一個單獨的子程序。
    已經從迷信轉爲理解的程序員們總會先懷疑自己的工作出了問題。
    如果你沒有陷入這種“拼湊加編譯”的怪圈,那就在你覺得合適的時候再去編譯吧。

第10章:使用變量的一般事項

    在你引入一個新變量的時候對它做出聲明,哪怕編譯器不要求你一定要這樣做。這樣做雖然不會捕捉所有的錯誤,但至少能發現其中的一部分。
    在靠近變量第一次使用的位置初始化它。
    一般而言,把對一個變量的引用局部化,即把引用點儘可能集中在一起總是一種很好的做法。
    儘可能縮短變量的“存活”時間。
    變量的存活時間開始於引用它的第一條語句,結束於引用它的最後一條語句。
    通過使用一些巧妙的方法,可以給一個變量賦予多種職責。不過你最好還是遠離這些奇技淫巧。
    早期綁定會降低靈活性,但有助於減小複雜度。晚期綁定可以增加靈活性,同時增加複雜度。

第11章:變量名的力量

    W.J.Hansen所做的一項研究表明,較長的名字適用於很少用到的變量或者全局變量,而較短的名字則適用於局部變量或者循環變量。
    lim不是一個合法的下標。它與last都是與first相對應的概念。不同之處是lim表示的是一個數組中並不存在的上界;而last表示的則是最終的、合法的元素。通常lim等於last+1。
    記住,名字對於代碼讀者的意義要比對作者更重要。
    在20世紀70年代,一條Fortran FORMAT語句中的句號錯寫成了逗號。結果科學家們算錯了太空飛船的軌道,導致了太空探測器的丟失---損失高達16億美元。
    

第12章:基本數據類型

    一條很好的經驗法則是,程序主體中僅能出現的文字量就是0和1。
    國際市場的重要意義正在日益突現,翻譯存放在字符串資源文件中的字符串要比翻譯存在於代碼中的字符串容易得多。
    不同於僅僅判斷一個布爾表達式,你可以把這種表達式的結果賦給一個變量,從而使得這一判斷的含義變得明顯。
    作爲一項一般性的原則,任何一種能夠對可能發生改變的事物進行集中控制的技術,都是減少維護工作量的好技術。
    用一款文本編輯器來尋找代碼裏的2、3、4、5、6、7、8、9,以確認你沒有由於不小心而使用了它們。
    在你習慣性的選用數組之前,考慮能否用其他可以順序訪問數據的容器類作爲替換方案---如集合、棧、隊列等。
    在C中結合ARRAY_LENGTH()宏來使用數組。
    創建自定義類型的最大優點,就在於它提供了介於你的程序和實現語言之間的一層絕緣體。
    你可能需要爲標準類型定義替代類型,以便讓變量在不同的硬件平臺上正確地代表相同的實體。
    

第13章:不常見的數據類型

    指針錯誤的症狀常常與引起指針錯誤的原因無關。因此,更正指針錯誤的大部分工作量便是找出它的位置。
    在C語言裏,在釋放內存區域之前用垃圾數據來覆蓋這些內存區域,可以讓與使用已釋放的指針有關的錯誤的表現方式更以致。
    一種常見的指針錯誤是“懸空指針”,即使用一個已經被delete或者free的指針。
    如果你還沒有養成使用auto_ptr的習慣,那麼就努力吧。
    強制類型轉換關閉了編譯器檢查類型不符的功能,因此在你的防禦式編程的鎧甲上挖了一個洞。一個需要很多強制類型轉換的程序在架構方面可能就存在一些需要修正的問題。
    即便全局數據並不總是引發錯誤,也很難將其作爲最佳的解決方法。
    如果你隨意使用全局數據,或者認爲不能隨心所欲地使用它們是一種約束,那麼你可能還沒有充分理解信息隱藏和模塊化的意義。
    你用全局數據能做的任何事情,都可以用訪問器子程序做的更好。使用訪問器子程序是實現抽象數據類型和信息隱藏的一種核心方法。

第14章:組織直線型代碼

    如果有人在閱讀你代碼的時候不得不搜索整個應用程序以便找到所需的信息,那麼就應該重新組織你的代碼了。
    如果代碼之間沒有順序依賴關係,那就設法使相關的語句儘可能的接近。
    

第15章:使用條件語句

    也許有時候你只剩下一種情況需要處理,於是就決定把這種情況編寫爲default子句(默認子句)。儘管這麼做有時候很誘人,但卻是不明智的。
    

第16章:控制循環

    一份針對程序員學員的研究把它和那些在開始或者結尾位置終止的循環做了對比。使用了帶退出循環以後,學員們在一份理解力測試中的得分提高了25% 。
    for循環的關鍵之處在於,你在循環頭處把它寫好後就可以忘掉它了,無須在循環的內部做任何事情去控制它。如果存在一個必須使執行從循環中跳出的條件,那麼就應該改用while循環去處理。
    避免空循環。
    不要爲了終止循環而胡亂改動for循環的下標。
    避免出現依賴循環下標最終取值的代碼。
    如果語言支持,請使用帶標號的break結構。
    如果你開始接受編寫簡單代碼這一原則,那就很少會寫出超過15或者20行的循環。
    研究表明,當嵌套超出3層以後,程序員對循環的理解能力會極大的降低。
    

第17章:不常見的控制結構

    不要用遞歸去計算階乘或者斐波那契數列。
    在某些情況下,一個完全知道goto不是好選擇的程序員,也會爲了增強可讀性和可維護性而選用goto。
    在很大程度上,軟件開發這一領域是在限制程序員對代碼的使用中得到發展的。
    

第18章:表驅動法

    表驅動法的優勢之一就是你可以把表裏面的數據存放在文件中,在程序運行時再讀取這些數據。這樣一來,就可以在不改動程序本身的情況下調整保險費率等參數。
    正如Butler Lampson, Microsoft公司一位傑出的工程師所說,最好是去找一種好的方案並且同時避免引發災難,而不要試圖去尋找最佳的方案。
    

第19章:一般控制問題

    請在布爾表達式的判斷裏採用true和false來代表真和假。如果你的語言並不直接支持這些寫法,那麼就用預處理宏或者全局變量來創建它們。
    與其寫一個龐大的、具有很多項的複雜判斷,還不如把中間結果賦給變量,讓你可以執行一個更簡單的判斷。
    用括號使布爾表達式更清晰。
    更好的做法是使用嵌套的判斷語句來表明你的用意,而不要依賴於求值順序和短路求值。
    按照數軸的順序編寫數值表達式。
    在C家族語言中,應該把常量放在比較的左端。
    爲空語句創建一個DoNothing()預處理宏或者內聯函數。
    過分深層的縮進,或者“嵌套”,已經困擾了計算機界長達25年之久,並且至今仍然是產生混亂代碼的罪魁禍首之一。
    結構化編程的核心思想很簡單,那就是一個應用程序應該只採用一些單入單出的控制結構。
    “程序複雜度”的一個衡量標準是,爲了理解應用程序,你必須在同一時間記住智力實體的數量。這種智力遊戲可以說是編程中最難的方面之一,這也使編程需要比其他任何活動都要專心。它也是程序員對“不時被打斷”特別的反感---這種打斷就相當於讓一位雜耍藝人一邊拋接三個球,一邊幫你照看商店。
    Edsger Dijkstra已經就複雜度的危險提出警告:“有能力的程序員會充分地認識到自己的大腦容量是多麼地有限;所以,他會非常謙卑地處理編程任務”。
    你可以通過做一些腦力練習來提高你自身的腦力遊戲水平。不過,編程本身的訓練就已經足夠多了。

第20章:軟件質量概述

    要讓所有特性都能表現得盡善盡美是絕無可能的。需要根據一組互相競爭的目標尋找出一套優化的解決方案,正是這種情況使軟件開發成爲一個真正的工程學科。
    明確設置質量目標是開發高質量軟件的一個簡單而清晰的步驟,但它常常被忽視。
    有的方法如代碼檢查,一舉可以確定問題的現象和原因;而另一些方法如測試,則只能發現問題表象,而要找到並從根本上修正缺陷還需要額外的工作。
    絕大多數項目的最大規模的一種活動就是調試以及修正那些無法正常工作的代碼。
    

第21章:協同構建

    CMU軟件工程研究所的調查表明,在設計過程中開發人員平均每個小時會引入1到3個缺陷,在編碼階段則會平均每小時引入5到8個,因此攻擊這些盲點就成爲了有效構建的關鍵。
    關於結對編程的早期報告指出,它的代碼質量能夠達到與正式檢查相近的水平。全程採用結對編程的成本可能比單人開發要高大約10%~25%,但開發週期大概會縮短45% 。雖然很多情況下這樣的結果相對於代碼檢查來說並無優勢,但卻大大超越了單人開發的效率。
    IBM發現,一小時的代碼檢查能夠節省大約100小時的相關工作。
    如果兩個人整天把時間浪費在爭論風格的問題上,那麼結對編程就不可能發揮它的威力。
    有時個人性格之間的衝突會導致組合的效能出問題,強迫無法配對的兩個人進行組合是毫無意義的,因此請對個性匹配的問題保持警覺。
    用於詳查的覈對表有助於集中注意力。由於詳查有標準的覈對表和標準的角色,因此它是一個系統化過程。
    不要指望公開演示能成爲提高產品技術質量的靈丹妙藥。
    

第22章:開發者測試

    測試永遠不可能徹底證明程序中沒有錯誤。
    測試本身並不能改善軟件的質量。測試的結果是軟件質量的一個指示器,但是結果本身並不改善軟件質量。想通過更多測試來改善軟件的質量,就跟妄想通過天天稱體重來減肥一樣。
    你必須期望在你的代碼裏有錯誤。儘管這種期望似乎有悖於常理,但是你應該期望找到這個錯誤的人是你,而不是別人。
    作爲正式測試方法的補充,優秀的程序員會使用各種不太規矩的、啓發式的方法去尋找他們代碼中的錯誤,其中一種啓發式方法就是猜測錯誤。
    正確設計的隨機數生成器可以產生你意想不到的、不尋常的測試數據組合。
    詳細完整的日誌記錄可以爲診斷錯誤提供幫助,還可以在產品發佈之後爲客戶提供有效的服務。
    許多人都遇到過這樣的事情:對同樣的數據測試了100遍,其中99次都成功了,但就是有一次失敗了。這種問題幾乎總是源於忘記對某處變量進行初始化了。
    存放以往錯誤的數據庫是另一種強大的測試工具,這樣一個數據庫既是管理工具,又是技術工具。
    除非在每次修改後重新對程序進行系統化的測試,否則要開發出一個高質量的軟件產品幾乎是癡人說夢。
    就重要性而言,測試應當與設計和編碼平起平坐,這就要求項目爲測試分配時間,重視測試並保障這一過程的質量。

第23章:調試

    你所面對的程序一定有一些東西需要你去了解。因爲如果你確實已經透徹地理解了它,這個程序就不應該還有缺陷,你應該早就糾正了這些缺陷。
    即使某個錯誤初看似乎不能歸咎於你,但出於你自身的利益,最好還是假設它的產生同你有關。
    程序員們在調試中陷入困境的一個原因是他們在一條死衚衕裏面走得太久。把要嘗試的事情列出來,如果某種方法不能奏效,那就換一種方法。
    使用代碼質量覈對表來激發你對可能發生的缺陷的思考。
    暫時放棄思考的好處是可以減少調試帶來的焦慮。不時潛入頭腦中的焦慮感是一個明顯的信號:到了該休息的時候了。
    人們往往會放棄讓缺陷無處遁形的徹底系統分析,而去進行快速的嘗試。我們每個人的投機心理都寧願去用一種有可能在五分鐘內發現缺陷的高風險方法,也不願意爲某種保證能找出缺陷的方法花上半個小時。這裏的風險就是如果五分鐘的方法沒有奏效,你也變得麻木了。一旦把使用這種“簡便”的查找方法作爲信條,那麼幾個小時也許就在無所建樹中流逝了,甚至是幾天、幾周、幾個月……有多少次你花上了兩個小時來調試原本只用三十分鐘就寫出來的代碼?
    不要輕信編譯器的第二條信息。
    “調試之魔鬼指南”說得很對:如果想讓自己的生活潦倒,讓自己的代碼質量一塌糊塗的最好方法,就是不懂裝懂地動手修補程序缺陷。
    忽略編譯器所提示的程序錯誤太過草率,關掉編譯器的警告功能則無異於掩耳盜鈴。
    你是否會把調試看做是能讓你更好地理解程序、錯誤、代碼質量和解決問題方法的良機?
    

第24章:重構

    演化一開始就充滿危險,但同時也是使你的軟件開發接近完美的天賜良機。
    專家們認爲,對未來需求有所準備的辦法並不是去編寫空中樓閣式的代碼,而是儘可能將滿足當前需求的代碼清晰直白地表現出來,使未來的程序員理解這些代碼到底完成了什麼功能,沒有完成什麼功能。
    有的重構的步伐比其他重構更大,到底什麼能算成是一次重構並不明確。因此請把重構的步伐放小點。
    事情很簡單:應該把簡單的修改當作複雜的修改。
    你是否同一時間只處理一項重構?
    你是否避免了將重構作爲先寫後改的代名詞,或者作爲拒絕重寫拙劣代碼的託詞?
    開發階段的重構是提升程序質量的最佳時機,因爲你可以立刻讓剛剛產生的改變夢想編程現實。請珍惜這些開發階段的天賜良機!

第25章:代碼調整策略

    對用戶來說,程序員按時交付軟件,提供一個清爽的用戶界面,避免系統死機常常比程序的性能更爲重要。
    Barry Boehm爲我們講述了一個故事,TRW公司開發一套系統,客戶最初要求該系統響應時間不能超過1秒。這樣的需求直接導致了一套極其複雜的設計方案以及1億美元的預算。公司經過進一步研究,認爲在90%的情況下即使是4秒的系統實際響應時間,也能滿足客戶需求。經過如此修改的系統需求爲公司節約了大約7000萬美元。
    在花費時間處理一個性能問題之前,請想清楚你的確是在解決一個確實需要解決的問題。
    性能問題的很多方面都是違反直覺的。
    如果沒有測量性能變化,那麼你想當然的優化結果不過是使代碼變得更爲晦澀難懂了。
    

第26章:代碼調整技術

    你可以盡情的使用可讀性更好的方法,而把代碼運行速度放到稍後來處理。
    事實上,在任何使用線性查找的場合你都可以使用哨兵法---從鏈表到數組。需要注意的是你必須仔細選擇哨兵值,並小心地把它們放到數據結構中去。
    編寫桌面應用程序的人可以對優化毫不關心,但那些爲嵌入式系統、實時系統和其他對代碼有着嚴格速度和資源限制的系統編寫代碼的人仍然可以從中獲益。
    

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

    如果你習慣於開發小項目,那麼你的第一個中大型項目有可能嚴重失控,它不會像你憧憬的那樣成功,而會變成一頭無法控制的野獸。
    方法論對於代碼質量的影響不大,對應用程序質量影響最大的通常是編寫程序的各個開發者的技能。
    

第28章:管理構建

    如果項目中有人要制定標準,那麼應該由一位受人尊敬的架構師來做,而不應該由管理者來做。在軟件項目中,“專家層”起的作用至少與“管理層”相同。
    版本控制對於團隊項目來說是必不可少的。當把版本控制、缺陷跟蹤和變更管理整合到一起的時候,其威力會更大。Microsoft公司的應用程序部門甚至認爲其專有的版本控制工具是一項“主要競爭優勢”。
    根據Fred Brooks定律,往一個已經落後的軟件項目里加入人手只會使它更加落後。
    但如果一個項目中的任務可以分割,那麼你就可以把它進一步分細,然後分配給不同的人來做,甚至可以分配給項目後期才加進來的人。
    如果進度落後了,那麼就調整“可選擇”和“有了更好”的優先級,並扔掉那些不重要的功能。
    程序員不僅在編程上花時間,也要花時間開會、培訓、閱讀郵件以及純粹思考。
    不同程序員在天分和努力程度方面的差別十分巨大,這一點與其他所有領域都一樣。
    不是所有編程項目的管理者們都會認識到,有一些編程問題與信仰有關。如果你是一名管理者,並且試圖要求統一某些編碼實踐,那麼就可能會激怒你的程序員。
    在軟件開發中,非技術出身的管理者隨處可見,具有技術經驗但卻落後於這個時代10年(以上)的管理者也比比皆是。技術出色並且其技術與時俱進的管理者實屬鳳毛麟角。如果你正在爲一位這樣的管理者工作,那麼就儘可能地保住你的工作吧。這可是非常難得的待遇。
    

第29章:集成

    增量集成有助於項目增長,就像雪球從山上滾下來時那樣。
    在階段式集成中,你一次集成許多組件,很難知道錯誤在哪。錯誤即可能位於其中任何一個組件,也可能位於組件之間的連接處。在增量集成中,錯誤通常要麼是在新的組件中,要麼是位於新組件和系統之間的連接處。
    “看到系統50%的功能已經能工作”總比“聽到編碼‘已經完成了99%’”更有說服力。
    無論你選用哪種集成策略,daily build和冒煙測試都是軟件集成的好方法。
    

第30章:編程工具

    據報道,複雜性分析工具在系統維護的生產率方面有大約20%的正面效應。
    在短時間內編寫高質量代碼的一種好方法是不要全部自己編寫,而去找一個開源的版本(或者購買一個 ).
    建造工具是編程的基本活動的一部分。
    如果你發現自己每天多次鍵入某個長度超過5個字母的命令,那麼它就是腳本或批處理文件不錯的候選者。
    就其本質而言,編程從根本上說就是困難的---即便有好的工具支援。無論能用到哪些工具,程序員都必須與凌亂的真實世界較力;我們須得嚴密地思考前後次序、依賴關係、異常情況;而且我們還要與無法說清楚自己想法的最終用戶交往。我們是重要應對連接到其他軟件或硬件的定義不清的接口,還要解決規章制度、業務規則以及其他複雜性之源,這些複雜性來自計算機編程之外的世界。

第31章:佈局與風格

    使代碼看起來有條理的最大意義莫過於展示出代碼的結構。
    不要將賦值語句按等號對齊。
    每行僅寫一條語句。
    每行只聲明一個數據。
    註釋的縮進要與相應的代碼一致。
    註釋的風格有力地影響着註釋是否具有負面作用。

第32章:自說明代碼

    解釋性註釋通常用於解釋複雜、有巧、敏感的代碼塊。在這些場合它們能派上用場,但通常正是因爲代碼含混不清,才體現出這類註釋的價值。如果代碼過於複雜而需要解釋,最好是改進代碼,而不是添加註釋。
    概述性註釋是這麼做的:將若干代碼行的意思以一兩句話說出來。這種註釋比重複性註釋強多了,因爲讀者讀註釋能比讀代碼更快。
    如果右邊的空間有充裕,行尾註釋對於數據聲明的標註正合適。
    當有人說“這些代碼富於技巧”時,我會認爲他們的意思是“這些代碼實在糟糕”。
    至少有一家公司根據自身實踐得出結論---對數據註釋比對使用數據的過程做註釋更重要。
    

第33章:個人性格

    假如你是軟件工程師,基本的建造材料就是你的聰明才智,主要工具就是你自己。
    老闆無法強迫你成爲好的程序員,很多時候他甚至無法判斷你是否合格。
    一旦決心成爲出色的程序員,你的發展潛力是很大的。各種研究發現,不同程序員創建某個程序所需的時間差異可達10:1;;同時還發現,不同程序員調試程序所需的時間、程序實現規模、速度、錯誤率和檢查出的錯誤數目也能達到10:1。
    要充分理解一個普通的程序,你得有很強的吸收細節的能力並同時消滅它們。如何專注你的聰明才智,比你有多聰明更重要。
    如果分配給你的工作淨是些不能提高自身技能的短期任務,你理應表示不滿。
    閱讀解決問題的有關方法。
    學習成功項目的開發經驗。
    現代語言產品一般都帶有大量函數庫,很有必要投入時間去瀏覽其說明。
    你願意閱讀本書就很值得稱讚。你已經學到了比軟件專業中多數人都更多的知識,因爲大部分程序員一年下來還看不完一本書。
    只要稍稍看一些書就會使你的專業知識又邁進一步。如果每兩個月能看一本計算機好書,大約每週35頁,過不了多久,你就能把握本行業的脈搏,並脫穎而出。
    熟練級的程序員對語言或環境(或兩者兼具)有着專業技能。這一級的程序員也許精通J2EE的盤根錯節,或者對《Annotated C++ Reference Manual》如數家珍。這些程序員都是所在公司的活寶,很多程序員再也不能超越該層次。
    技術帶頭人具有第三級的專業才學,並明白編程工作中只有15%用來和計算機交互,其餘都是與人打交道的。程序員一般只花30%的時間單獨工作,與計算機交互的時間則更少。技術帶頭人會爲人寫代碼,而非爲機器。真正高手所寫的代碼,像水晶一樣晶瑩剔透,還配有文檔。他們可不會浪費其寶貴的腦力,去重新組織用一句註釋就能說清楚的某塊代碼邏輯。
    當初學者或中級程序員不是錯,當熟練級程序員而非技術帶頭人也無可厚非。但如果知道自己該如何改進後,還總是在初學者或者中級程序員階段徘徊,就是你的不對了。
    力圖理解編譯器的警告,而非置之不理。
    透徹理解自己的程序,而不要只是編譯看看能否運行。
    如果你工作10年,你會得到10年經驗還是1年經驗的10次重複?必須檢討自己的行爲,才能獲得真正的經驗。只有堅持不懈地學習,才能獲取經驗;如果不這樣做,就無法得到經驗,無論你工作多少年。
    好習慣很重要,因爲程序員做的大部分事情都是無意識完成的。
    行爲養成習慣,年復日久這些好壞習慣就決定了你作爲程序員的優劣。
    比爾蓋茨說,任何日後出色的程序員前幾年就做得很好。從那以後,程序員好壞就定型了。在你搞編程頗有些年頭後,很難會突然說“怎樣才能使這個循環再快些呢?”或者“如何讓這段代碼更好看懂呢?”優秀的程序員早就養成了這些習慣。
    

第34章:軟件工藝的話題

    軟件往往要通過實證而不是證明,這意味着它就得反覆測試和開發,直至能正確解決問題爲止。
    要對編程問題找出最有效的解決方案時,盲目迷信某種方法只會縮小你的選擇餘地。
    由於“工具箱”形象地比喻出抽象的折中思想,所以這個比喻很有用。
    軟件開發中許多頑固的方法源於對錯誤的畏懼心理。“試圖沒有錯誤”是最大的錯誤。

第35章:何處有更多信息

    你犯的錯誤別人早已犯過,要是不想自討苦吃,就讀讀他們的書吧,這樣能夠避免再走彎路,並找出解決問題的新方法。

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