本文提到的絕大多數錯誤,都是做者歷經一番艱辛才得以發現,要麼是由於本身犯過,要麼是在別人的工做中見過。程序員
本文並不是意圖對程序員劃分等級,只是適合某些程序員閱讀,他們相信本身有能力判斷一件事情在什麼狀況下是不良習慣的跡象,在什麼狀況下則是特殊環境致使的結果。正則表達式
寫這個系列是爲了迫使做者自省,而發佈出來,是由於以爲你們也可能會從中找到感興趣的地方。算法
對代碼進行推理意味着能跟隨代碼的執行路徑(「在腦子裏運行程序」),同時清楚地知道代碼執行的目標。數據庫
特徵編程
補救措施小程序
程序猿能夠經過實踐來克服這個缺點,若是 IDE 自帶的調試器能單步調試,就把它做爲助手使用。好比說在 Visual Studio 裏,這就意味着要在問題區域的起始處打上斷點,而後按下‘ F11 ’單步調試,查看變量的值(變化先後都要查看),直到你明白了代碼正在作什麼。若是你的目標環境不具有這種特性,那就找一個擁有這種特性的環境去實踐。數組
這麼作的目的是,讓你作到再也不須要調試器就能在腦子裏跟隨代碼的流程,並且有足夠的耐心去思考代碼正在對整個程序的狀態作什麼。這麼作的好處就是可以識別出冗餘且無用的代碼,並且不須要從頭執行整個路徑就能在當前代碼中找出 bug。瀏覽器
面向對象編程( Object Oriented Programming )就是一種語言模型,正如函數式編程( Functional programming )或聲明式編程( Declarative programming )同樣。它們每個都和過程式或命令式編程有着顯著不一樣,就像過程式編程明顯不一樣於彙編或基於 GOTO 的編程。此外,雖然有不少語言都跟隨同一個主流編程模型(如面向對象的編程),但它們都只介紹本身的改進,例如遞推式構造列表( list comprehensions )、泛型( generics )、鴨式分類( duck-typing )等等。緩存
譯者:duck-typing 是動態語言的一種程序設計風格,用以實踐方法多態。Duck-typing 並不關注對象的實際類型,而是關注其表現。概念提出者 James Whitcomb Riley 這樣描述這個風格:當看到一隻鳥走起來像鴨子,遊起泳來像鴨子,叫起來也像鴨子,那這隻鳥就能夠看出是鴨子。安全
特徵
補救措施
若是你的技能不足,是由於別人教得很差或是本身沒學好,那編譯器自身就是一位備選老師。學習一個新的編程模型,最有效的 辦法莫過於建立一個新工程,無論都有哪些新的構造方法,強迫本身去使用它們,不管在工程中的使用是否明智。你也須要練習用本身最熟悉且通俗易懂的措辭來解 釋模型特性,而後遞歸地建立本身的新詞彙表,直到你對模型理解入微。舉個例子:
階段一:「OOP 就是方法的集合」
階段二:「OOP 裏的方法就是函數,它們運行在自帶全局變量的小程序中」
階段三:「全局變量被稱爲字段,其中有些是私有字段,在小程序外不可見」
階段四:「擁有私有和公有元素是爲了隱藏實現細節,暴露乾淨整潔的接口,這就叫封裝」
階段五:「封裝意味着實現細節不會破壞業務邏輯」
對全部編程語言來講階段五看起來都同樣,由於全部語言在階段五都試圖讓程序猿能表達出程序的意圖,而不須要將其隱藏在如何實現的細節之中。拿函數式編程再舉個例子:
3.缺少研究技巧/長期缺少對平臺特性的瞭解
現在,現代語言和框架都帶有很是了不得的內置命令和特性,一些主要的框架(像 Java 、 . Net 、 Cocoa)因爲自己結構龐大,任何一個程序猿(甚至是一個很優秀的程序猿)都要花費好幾year時間去學習。可是,一個優秀的程序猿在本身開始構造所需函數以前,會先搜索有沒有知足需求的內置函數。而傑出的程序猿們則可以分解並識別出任務中的抽象問題,接着在實際開始設計程序以前,去搜索適用的現有框架、模式、模型和語言。
特徵
若是在應該掌握新平臺好久之後,這些特徵還繼續出現,那它們就暗示着存在問題。
譯者:「 comfort zone 」就是令人感到安全、舒服或在其掌控之下的形式或狀態。
也會偶爾複製代碼,複製的頻率和框架大小成比例,所以,按本身的程度來判斷吧。手寫鏈表的人也許知道本身正在作什麼,但手寫 StrCpy() 的人可能就不知道了。
補救措施
一個程序猿若是不放慢速度,就不可能學到這類知識。並且頗有可能,這我的一直都在火急火燎地用任何須要的手段讓每一個函數 都工做起來。他須要在手邊放一本平臺的技術參考手冊,而且可以花最小的代價瀏覽它,這就是說要麼在桌上的鍵盤右邊放一本打印稿,要麼還有一個屏專門用來打 開瀏覽器。爲了開始培養這種習慣,他應該重構舊代碼,目標是減小十分之一以上的指令數量。
若是你不能理解指針,那你能寫的程序類型就很是有限,由於指針的概念創造出了不少複雜的數據結構和有效的 APIs。託管類語言使用引用來代替指針,二者很像,但引用增長了自動解引用功能並禁止指針運算,從而消除特定類型的 bug。不管如何,它們仍是很是類似,不能掌握這個概念就會致使數據結構的設計不好勁,而且出現一些因爲不理解方法調用中值傳遞和引用傳遞的區別而致使的 問題。
特徵
補救措施
「我有一個叫 Joe 的朋友待在賓館的某個房間裏,而我不知道他的房間號。但我知道他的熟人 Frank 待在哪一個房間」,所以我跑去敲門問他‘Joe 在哪一個房間?’,Frank 表示他也不知道,但他知道 Joe 的同事 Theodore 在哪一個房間,並給了我 Theodore 的房間號。所以我又跑到Theodore的房間問 Joe 在哪,Theodore 告訴我 Joe 在414房間。實際上,Joe 就是在那個房間。」
對於指針,能夠用不少種不一樣的隱喻來描述,而數據結構則能夠描述成多種比喻。上面是對鏈表的簡單類比,並且任何人都能發 明本身的版本,即便他們不是程序猿。提到指針你們都能理解,所以,你的描述不會比現有的描述還更全面。當程序猿試圖想象計算機的內存里正在發生什麼,並把 這個想象和他們對普通變量的理解融合時,雖然這二者很類似,但這個時候就會沒法理解。也許將代碼解釋成一個簡單的故事有利於推理當前的情況,直到發現其中 的區別,直到程序猿能夠像面對標量值和數組同樣直觀地想象指針和數據結構。
遞歸的思想很容易理解,但程序猿們常常在本身腦子裏想象一次遞歸操做的結果時遇到困難,或想不通一個簡單函數是怎麼計算出複雜結果的。這些不解使得要設計一個遞歸函數變得難上加難,由於當你要對初始條件或遞歸調用的參數進行測試時,你想象不出「當前走到哪一步了」。
特徵
補救措施
先體會一下,準備好迎接某種堆棧溢出吧。首先,在代碼裏只寫一個初始條件檢測並只調用一次遞歸,遞歸中使用同一個被傳遞 的未修改參數。即便你以爲寫得不夠好也要停下來,不管如何,讓代碼運行一下。它拋出了一個堆棧溢出的異常,那麼如今返回去繼續寫,在遞歸調用中傳遞參數的 已修改拷貝。產生了更多的堆棧溢出錯誤?輸出過分?那就接着反覆修改代碼再運行,從修改初始條件測試轉向修改遞歸調用,直到你開始憑直覺就知道函數怎麼轉 換它的輸入參數。忍住衝動,使用的初始條件測試或遞歸調用不要超過一次,除非你真的知道本身在作什麼。
你的目標是敢於進行遞歸調用,即便在這條想象中的遞歸路徑上,你沒有徹底搞清楚「本身在哪裏」。那麼,等你須要爲一個真正的項目去寫一個函數時,你會從寫單元測試開始,而且運用上面提到的相同技術來一步步推動。
特徵
補救措施
別人是按代碼行數付錢給你嗎?這些舊習慣是否是你從一個擁有弱類型體系的語言中延續下來的?若是兩種都不是,那這種狀況 就相似於「沒法推理代碼」,可是彷佛不是推理能力受損,而是沒法信任和適應編程語言。有些特徵更像是經不起邏輯分析的「comfort code」,但程序猿非要強迫本身這麼寫。惟一的補救措施就是,多花時間熟悉編程語言。(譯者:由於不熟悉因此感到不肯定,所以非要寫這樣的代碼才能安心。)