《代碼大全》讀書筆記

來源:互聯網
上載者:User
  • P7
    把主要精力集中於構建活動,可以大大提高程式員的生產率。
    在最近的一個項目中,對於這一點,我是深有體會。我們花了很長的時間做設計,結果接下來的許多工作都在愉快的心情下完成。我覺得
    P28
    的那個食物鏈的例子更有說服力,健康的生態環境中,海鷗吃新鮮的鮭魚,鮭魚吃新鮮的青魚,青魚吃新鮮的水蝽。這是一條健康的食物鏈。
    如果環境被汙染了,水蝽在汙染的水域遊泳,那麼海鷗,食物鏈的最後一環吃下的不僅僅是是不健康的鮭魚體內的垃圾,還有青魚,水蝽體內的汙染物。軟體開發中,架構師吃掉需求,設計師吃掉架構,程式員,軟體食物鏈的最後一環,消化掉設計。如果一開始就被汙染了,我們就不要指望程式員快樂了。整個軟體都會具有放射性,周身都是缺陷,絕對導致程式員脾氣暴躁、營養失調。在我們規模不大的團隊裡,一個人身兼數職,傷害更大。所以,項目一開始就決定了它能否成功。

  • P7
    原始碼——往往是對軟體的唯一精確描述
    其實我們不必為沒有精確的文檔沮喪,不是嗎?

  • P13 常見的軟體隱喻
    好的隱喻可以讓我們思考更多的問題,並走上正確的道路。我們是在
    Writing Code,還是 Growing a System 還是 System Accretion
    或是 Building Software ?
    做不同軟體有不同的方法,不要拘泥。

  • P24
    避免使用錯誤的方法製造正確的產品
    往往我們在軟體開發中會很強調測試。的確、測試是品質的保證。但是測試只保證有品質的代碼,卻不保證有品質的設計。

  • P42 需求的 checklist
    其實我們不必去照本宣科的寫需求分析書什麼的,做需求分析即使是在大腦中,口頭交流上完成,也是有這麼一個過程。落下文字固然是好的,但並不是重點。關鍵在於做不做。
    否詳細定義了系統的全部輸入,包括來源、精度、取值範圍、出現頻率。是否詳細定義了系統全部輸出,包括目的,精度,取值範圍、出現頻率,格式?是否定義了
    機器記憶體和剩餘磁碟空間的最小值?是否詳細定義了系統的可維護性,包括適應特定功能的變更、作業環境的變更、與其他軟體的介面的變更能力?

    書中列的遠比我這裡列出的多,非常值得一讀。定下這些是很重要的,我覺得合理的遊戲開發,是有一個相對穩定的策劃方案,和一些已經完成完成的美術資源。大
    部分的變更都留在下一版本去做。策劃和美術永遠為下一個版本工作,而程式可以根據相對穩定的需求做設計。這樣做,即使第一個版本是不可玩的,扔掉,也是能
    讓遊戲最終成功。

  • P46 資料設計
    我曾經很迷惑項目文檔到底要寫什嗎?這裡列舉的一些東西解開了我一些疑惑。如果你選擇使用一個順序訪問的列表表示一組資料,就應該說明為什麼順序訪問比隨機訪問更好。(往往隨機訪問更為高效)在構建期間,這些資訊讓你能洞察架構師的思想。在維護階段,這種洞察力是無價之寶。
    後面 P58 有個更為有趣的例子:Beth 想做丈夫 Adbul
    家祖傳的燉肉。Adbul
    說,先撒上胡椒和鹽,然後去頭去尾,最後放在鍋裡蓋上蓋子燉就好了。Beth
    就問了,“為什麼要去頭去尾呢?” Abdul
    回答說,我不知道,我一直這麼做,這要問我媽。他打電話回家一問,母親也說不知道,她一直這麼做,這個問題要問奶奶。母親就打了個電話給奶奶,奶奶回答說,“我不知道你為什麼要去頭去尾,我這麼做是因為我的鍋太小了裝不下”:D
    架構應該描述所有主要決策的動機。

  • P48 國際化和本地化
    國際化常常被稱為 i18n 是因為 Internationalization
    這個單詞太長了,I 和n 之間有 18 個字母。
    同理,通常本地化簡寫為 l10n
    。這個工作一定要在構架期想好啊,到底我們需不需要
    i18n 或者支援 l10n 就夠了,到底用 UTF-16 還是 UTF-8
    還是 ascii
    串就可以。是代碼中嵌入字串,還是封裝到一個類,還是可以放去一個設定檔。我們現在的項目,一開始考慮岔了,結果後來花了很多精力重構。好在發現問題不
    算晚,否則代價更高。引申的想一想,發現問題我們應該最早的解決,不要怕做出錯誤的決定,因為更可怕的是,意識到錯誤後,因為害怕修正錯誤的代價過大,而
    一拖再拖。代碼的 bugfix
    大家都知道應該立刻做,但是設計失誤卻容易被放過,將就著做下去。

  • P51 過度工程
    這個問題把握好並不容易。一方面,我們希望系統健壯,如果組成系統的各個部分只在最低限度滿足健壯性要求,那麼整體通常是達不到要求的。軟體健壯性不取決於最薄弱的一環,而是等於所有薄弱環節的乘積。構架應該指出每個部分,程式員為了謹慎而寧可做過度工程,還是做出簡單的能工作的東西就夠了。有些東西是不應該過分花精力的,這個錯誤我們也犯過,尤其一些一開始就知道以後很可能要重構的部分,大量的精力花在裡面很浪費。

  • P62 選擇程式設計語言
    我曾經也覺得 C++ 是萬能的,這種想法很多 C++
    程式員也有。但是無可否認,每種語言的表達力是不同的。書在這頁有一張表,如果
    C 的表達能力是 1 的話,C++ 和 Java 就是 2.5 。而 perl
    和 python 卻有 6
    。這就是我們選擇遊戲邏輯指令碼編寫的原因之一。另外對語言的熟悉程度是很影響程式員的效率的,所以我們不能獨立的看語言本身的表達能力。P63
    有個例子,用一群 Fortran 背景的程式員去用 C++
    編寫一個新系統,結果他們編寫出的是偽裝成 C++
    的 Fortran 代碼。他們扭曲 C++ 來類比 Fortran
    的不良特性並且忽略了 C++
    豐富的物件導向能力。我們這裡有個現成的例子,一個
    C++ 程式員用 C++/C 的方式寫 Lua
    ,結果可想而知。到現在我還在叮囑他,一定要理解,再理解
    Lua 。lua
    不是 C 。

  • P68 Programming into a Language
    注意,這裡是 into 而不是 in 。書這裡用了一個 vb
    的例子來說明,恰好我也有個例子。我們現在用 C++
    構建系統,C++
    裡有個相當麻煩的東西,就是單件的生存期問題。一個
    singleton
    到底什麼時候建立出來,什麼是否析構,相信很多
    C++
    程式員在構建大系統的時候都頭痛過。據我所知,我們公司別的項目的同事到現在還在頭痛這個問題。這次我做了一個約定,禁止任何模組的代碼構造靜態對象,也就是說,任何在
    main 函數前自動的物件建構過程和 main
    函數之後的自動析構過程都是不允許的。然後我們有一整套管理單件的方法供使用,這個問題被很好的解決了。我們再也沒有為某個單件什麼時候構造出來的,或是為什麼他提前析構了的問題煩惱過。

  • P78 管理複雜度的重要性
    我們做軟體,就是在和問題的複雜度做鬥爭。有三個問題需要注意:用複雜的方法解決簡單的問題;用簡單但錯誤的方法解決複雜的問題;用不恰當的複雜方法解決複雜的問題。

  • P80 high fan-in 和 high fan-out
    高內聚,低耦合很容易被重視。但是高扇入低扇出有時候會被忽略。這裡是說,我們應該盡量的大量的使用某個低層次上給定的類(high
    fan-in)
    而每個類都應該盡量少使用其他的類(控制在7個之下)。

  • P83 子系統間盡量減少聯絡
    書裡的例子很貼切。一個通用的規則是,如果 A
    系統用到 B ,B 又用到 C ,那麼 C 就不要再用到 A
    了。系統層的設計圖應該是無環圖。

  • P91
    封裝不僅僅是有簡化過的模型看到複雜的概念,而且同時還不能讓你看到複雜概念的任何細節
    隱藏資訊的重要性毋庸質疑。所以我們現在不僅用
    C++ 的 private
    隱藏資訊。還用介面的方法,不在標頭檔暴露任何設計細節。另外,任何一個不滿足現狀的程式員,對自己以前的代碼一定不會滿意。但是複用老的不滿意的代碼並非壞事。我們需要做的是,重用的時候,把老的東西隱藏起來。

  • P99 預料不同程度的變化
    好的設計人員應該對以後可能變化的部分很敏感。這點很有體會,我自己就是這樣一步步過來的。做一個決定之前,想想如果他做錯了的後果。估計未來改變的成本,以作出合適的設計,非常的重要。系統為了適應每種變化而做過度設計又是不合適的。到底怎樣設計,需要良好的感覺。

  • P101 耦合的種類
    鬆散耦合是每個系統設計人員所追求的東西。但是其標準往往把握不準。舉個簡單的例子,不一定恰當。在我最早的設計裡,系統把座標這個東西封裝成一個叫做
    point ,以後參數傳遞都傳 point * ,而不是 x,y
    。這看使很合理。但是,這的確增加了耦合度。因為每個類都需要知道
    point
    的細節。很多情況下,用簡單類型做參數傳遞反而更合適。(到底傳
    point * 還是 x,y 依舊要根據實際情況靠量)
    參數過多也會導致耦合度的增加,從這個角度看,x,y
    是兩個參數, point *
    是一個參數。關於耦合程度的問題,沒有絕對唯一的標準。書裡的闡述和總結非常值得一看。

  • P103 查閱常用的設計模式
    設計模式這個東西,給程式員帶來的最大的好處就是增加了交流的便捷。一個人可以思考的深度取決於他用于思考的語言掌握的詞彙量。設計模式也可以給程式員帶
    來這種便捷。其實常用的設計模式,即使你沒有看過《設計模式》這本書,只要你是一個經驗豐富的程式員,這些估計大多考慮過。但是,給設計模式起個名字,卻
    可以加快夥伴間的交流。不過必須警惕一個陷阱,那就是為模式而模式。強迫代碼去使用某個模式是很危險的。

  • P106 避免失誤
    失敗的經驗比成功的經驗重要。如今網遊開發尤甚。我們的遊戲能成功,是因為我們在失敗後吸取教訓,小心謹慎。我們的代碼未必品質高很多,但是很多業內同仁做出的東西吸取了許多人家成功的經驗卻失敗了,正是因為他們缺少對失敗教訓的學習。

  • P111
    自上而下和自下而上的設計方法
    很多人都贊同自上而下的設計方法,把問題逐步分解,再分而治之。而我喜歡至下而上的設計方法。先把絕對要做的模組做了,再考慮怎麼把他們搭起來。但是我在
    實際操作的時候往往是,自下而上的做,自上而下的思考。書這裡對兩種方法的優缺點的總結非常精闢。我特別同意最後的結論,這兩種方法並不是互斥的——你會受益於二者的相互協作。

  • P114
    開發人員不把原型代碼當作可以拋棄的代碼。
    這個問題很嚴重,我多次看到過了。做原型真的是一個好方法,但一定要明白,有些代碼寫出來就是為了以後扔掉的。

  • P118 使用數位相機
    設計文檔不一定要詳細的寫成規範的文檔,重要的是我們做了設計並保留下來,而不是寫出漂亮的文檔(去蒙投資?)數位相機似乎是一個好東西,這樣可以把草稿
    紙上的亂塗亂畫保留下來。我一直覺得鉛筆是設計師最好的夥伴。開會的白板也是,雖然現在已經有電子化的白板,但是,昂貴的東西不是所有開發人員都需要的。

  • P131 不要讓 ADT 依賴於儲存介質
    這點早就意識的到,類裡面最好不要有 readfromfile
    , writetofile
    這樣的方法。但是為了方便,往往又會加上這些方法。最終的結果是,依賴檔案帶來的不便總是比便利要多。同理,依賴檔案名稱也是不恰當的。可悲的是,有些錯誤犯一次往往不夠。意識到這樣做的不好是很容易的,真正杜絕它是另一件事。

  • P143 在萬不得已時通過 private
    繼承來實現“has a”的關係

    private
    繼承的主要原因是讓外層的類能夠訪問內層被包含類的
    protected 成員。根據我自己的經驗,讓類有 protected
    成員本身就不是一件好事。我剛學 C++
    的時候,很多類都有大量的 protected
    ,但現在,只剩下了 private 和 public 。雖然偶有
    protected
    ,但是我相信,那些都是可以通過改良設計然後去掉的。我不喜歡教條,如果有教條說,不準用
    protected ,我會很反感。而實現上,我的感覺會排斥
    protected 的使用。

  • P174 對於超過 200
    行代碼的子程式來說,沒有哪項研究發現它更夠降低成本和/或降低出錯率

    這是個誰都懂的道理,但是錯誤我自己也犯過。早幾年,我為大話西遊編寫了一個圖文混排的函數,可以在聊天或是其他螢幕文字中同時顯示不同的文字,有不同的
    顏色,狀態,效果,還可以顯示動畫表徵圖。當時圖一時方便,還有一些效率原因,我寫了一個幾百行的函數。再接下來的幾年裡,我為這個函數多次頭痛過,出過不
    只三次的 bug
    。直到在夢幻西遊的項目裡,這些模組得到全部重寫才舒了一口氣。長達千行的子程式我也在一些地方見過,那也是非常可怕的。並非所有程式員可以真正認識到這
    樣做的可怕程度吧。

  • P176 使用 C++ 中的 const
    關鍵字來定義輸入參數

    能夠正確充分的使用 const 是合格的 c++
    程式員評判標準之一。

  • P176
    不要把子程式的參數用做工作變數
    不要因為想當然的效率因素使用輸入參數做工作變數,編譯器會幫你最佳化的。如果這樣做,至少在我們公司內部的
    codeview 上是要被嚴重警告的。

  • P210
    確認留在代碼中的錯誤訊息是友好的
    讀這段的時候想到同事給我講的一個故事:他以前的同事因為敲出髒話被開掉了,起因是他的老闆(也是一個程式員)在調試代碼的時候被某個模組的出錯資訊罵了一通

  • P220
    通過虛擬碼編程過程建立子程式
    並非所有的代碼編寫前都要有虛擬碼的。更古老一點的編程方法裡,要求編寫程式前先畫框圖。這種方法我也學習過,但始終不覺得自然。虛擬碼如果也有條條框
    框,說不定我也不會去用。但是,實現複雜演算法時,這種方法依舊被我使用的最多。一個附帶的好處就是,先有了注釋,然後刪掉多餘的;而不是先有代碼,再加上
    注釋。

  • P238 資料認知測試
    哈哈,我得了 28 分(或者是 27.5)
    ,而且沒有被作者的陷阱框住。

  • P240
    隱式變數聲明對於任何一種語言來說都是最具危險性的特性之一
    早年,我們用 Lua 的第 4
    版寫指令碼,結果在這個問題上吃過幾次大苦頭。好在
    lua 在第 5 版後增加了 metatable
    可以有辦法避免這個問題。那就是給 _G 定義一個
    __newindex 的 method 。例子在 lua 包裡的 test
    目錄下可以找到,pil 裡也應該有提到。

  • P244 Rob Pike 建議使用 0xDEADBEEF
    這一常量來填充記憶體,因為在調試器裡很容易識別它。

    其實還可以用 0xBADF00D 也很有趣,可惜是 7
    個字母。有個朋友用這個做網名,我還蹭過一頓飯。:D

  • P246
    儘可能縮短變數的存活時間
    這是一個淺顯但實用的道理。p248
    裡還提到,全域變數的存活時間最長,就憑這一點,我們也應該避免使用。使用全域變數和不使用,是關易寫代碼和易讀代碼的區別;也是“方便性”和“智力可管
    理性”的理念區別。這一節還有個被量化的概念,變數的跨度。把一個臨時變數重複使用,在增加了存活時間的同時也增加了其跨度,所以出現了很糟糕的味道。這
    一點在 P255 又談了一次。

  • P253 綁定時間
    一般說來,變數的綁定時間越晚,靈活性越好。這裡的關於綁定時間的總結很不錯。分別為:

    • 編碼時綁定 (使用 magic number)
    • 編譯時間綁定 (使用命名常量)
    • 載入時綁定 (讀註冊表,設定檔等)
    • 對象執行個體化時綁定 (建立對象時讀入)
    • 即時 (每次操作時讀入)

    綁定越晚,在增加靈活性的同時,也增加了耦合度。

  • P256 避免讓代碼具有隱含含義
    這裡有個例子其實我曾經也類似的幹過,變數
    pageCount
    在非負的時候表示已列印紙張的數量,否則,-1
    表示有錯誤發生。這在技術領域裡被稱為“混合耦合”是應該避免的。pageCount
    客串了一個 boolean
    類型。如果真的想節省一個變數的空間的話,我覺得使用
    union 可能能好些。
    1. union {
    2.         int pageCount;
    3.         struct {
    4.                 unsigned int unused:31;
    5.                 unsigned int pageError:1;
    6.         };
    7. };

或許這也不是個更好的方案。

  • P298
    在程式生命期中儘早決定國際化/本地化策略
    關於這個問題,我們吃過的苦頭足夠說明問題。另外,MS
    倡導的 _T()
    宏不一定是一個優秀的方案。如果要支援 unicode
    ,UTF-8 是個不錯的選擇,雖然不總是最好的。UTF-8
    表示漢字時 50% 超出的儲存空間好好考慮一下。

  • P301 更安全的做法是使用 strlcpy() 或
    strcpy_s()

    很高興看到這句話是在譯註裡列出,可見譯者是用了心的。類似的譯註,我在侯傑的譯書中見過。不過
    strlcpy 和 strncpy 的行為還是有區別的,strcpy
    往往可以簡單的用 strncpy 取代,但是 strlcpy
    卻沒有這個效果。不過我也傾向於用 strlcpy ,它同
    strcpy 一樣的高效,而且的確也更安全。strncpy
    在參數為 NULL 時會出錯。

  • P305 把 enum
    的第一個元素留做非法值

    這是這本書讀到現在碰到的第一個以前沒想過的技巧。一直我都是把非法值定義成最後一個
    enum
    值,或者定義成一個很大的特殊數字。書這裡的道理很充分,因為一些沒有合理初始化的變數往往是
    0 ,把 0 作為非法值更容易捕捉到錯誤。

  • P309 Monty Python 為 20 世紀 60
    年代英國經典電視連續劇,python
    語言由此得名。

    還是譯註,很有趣 我以前並不知道 python 的名字是怎麼來的。

  • p329 簡化複雜的指標運算式
    上學的時候,我經常寫
    p->q->r->s.data
    這種運算式,當我工作後,被這種運算式坑過幾次後,用額外的指標變數來提高代碼清晰度(p327)
    同樣成了我的信條。另外 p329
    介紹的畫一個圖的方法也是被經常使用。

  • P330
    分配一片保留的記憶體後備地區
    用樣來保證程式崩潰的緊急處理所需要的記憶體是個簡潔的方法。

  • P364 濫用case語言
    把不同的控制結構混在一起用的確是非常糟糕的習慣。不過有時候的確可以帶來高一些的效率。我們可以不用它,但是不能讀不懂它。這讓我想起上次去跟同事一起出差,在火車上他談到的一個例子:
    1. int n = (count + 3) / 4;
    2. switch (count % 4) {
    3.     case 0: do {  
    4.         dosomething0();
    5.     case 1:
    6.         dosomething1();
    7.     case 2:
    8.         dosomething2();
    9.     case 3:
    10.         dosomething3();
    11. }
    12. while (--n>0);
    13. }

P374 在 while 迴圈更適用的時候,不要使用
for 迴圈

  • 道理早就明白,可是就是老不遵守
    我想還是因為懶。

  • P376 一個迴圈只做一件事情
    我在自己的書中談效能時也提到過這一點,其實,合并幾件事情在一個迴圈做不一定可以得到更高的效能。

  • P377
    避免出現依賴迴圈下標最終取值的代碼
    還是一個早就明白的道理,但是錯誤一犯再犯。

  • P384 for 迴圈變數的範圍
    VC 中的語義跟 C++ 標準定義的不一樣。MS
    推薦一種解決方案:
#define for if (0); else for 
 

  • P397
    如果為我工作的程式員用遞迴去計算階乘,那麼我寧願換人
    說的好。如果我帶的新同事在 C
    語言裡用遞迴計算費伯納西數列,估計我也會教育一下的。

  • P399 讓代碼不包含 goto
    並不是目的,而只是結果,把目的地組中在消除 goto
    上面是於事無益的。

    p408
    還有一句,如果程式員知道存在替換方案,並且也願意為使用
    goto 辯解,那麼用 goto 也無妨。

  • P408
    軟體開發這一領域是在限制程式員對代碼的使用中得到發展的
    這裡舉的例子,允許根據行數或者標號調用子程式,是我早年用
    basic 時夢寐以求的。不過那個時候 basic
    過於簡陋,期待這種功能是迫不得已。實在不行的時候還要自己擴充
    basic 解譯器。

  • P429
    最後是去找一個好的方案而且同時避免引發災難,而不要試圖去尋找最佳的方案。
    我想補充,如果你知道一種更好的方案但是不能完全避免災難的時候,請注釋這種更好的方案。

  • P442
    這項建議(在等於運算式中的常數寫在前面以避免把
    == 錯誤的敲成 =
    的問題)與按造數軸排列的建議相衝突。我個人偏向於使用數軸排序法,讓編譯器來告訴我有沒有無意寫出的指派陳述式。

    我也不希望把常數寫在前面,但老說不清楚原因。

  • 關於剩下的 400
    :代碼大全》的確是本好書,雖然厚達九百頁,但是卻沒有什麼廢話。讀書筆記只做到這
    裡,雲風也意尤未盡的看完了書的最後一頁。缺少後一半的讀書筆記並不是說書的後半本不及前半本精彩。相反,我認為後面的某些章節對我的啟發更大。只是,後
    半本書是我花了半個月時間,每天臨睡前躺在床上看完的。早上起來,已經懶的在機器上敲下什麼文字了。喜歡這篇讀書筆記的同學,推薦去買一本《代碼大全》耐
    心的閱讀全部。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.