1. 面向編譯器寫程式碼,而不是面向使用者
當人們使用編譯器建立自己的 app 時,在把自己的想法訴諸於機器程式碼的過程中,常常會將那些可以使得程式設計更為簡單卻又冗長的語法遺忘於腦後。
無論你使用的是單字母的識別符號還是更易於人腦理解的識別符號,對於編譯器而言,毫無區別。編譯器不在乎你寫的是否是優化表示式,也不在乎你是否用括號封裝了子表示式。編譯器要做的就是將這些人腦可讀的程式碼,解析為抽象的語法樹,並將這些樹轉換成機器程式碼,或某種中間語言。
那麼,為什麼不使用更可讀或者語義更明顯的識別符號呢——而不要僅僅是I,J或x。老實說,現在我們用來等待編譯器完成轉換識別符號的時間幾乎是微不足道。但是,這麼做卻可以大大減少你和其他程式設計師用於閱讀理解這些原始碼所用的時間。
還有一個類似的觀點是:或許你可能已經記住了相關的運算子優先順序,於是省略了表示式中一些不必要的括號,但是卻沒有考慮到後面的程式設計師有可能會誤讀你的程式碼,並就它是如何工作的作出一些無效的假設。
我的想法是,假設大家都知道,乘法(或除法)優先於加法和減法。其他任何我放到表示式中的內容我都會用上括號,以確保能真正表達我的意思,其他人也能真正理解我的想法。
有研究表明,有的程式碼維護所需要的時間甚至超出其編寫時間的五倍以上。所以將程式碼寫得易於閱讀和理解是非常有意義的。
2. 函式方法過於龐大
有一個經驗法則就是,我們寫的程式不應該過於龐大。而且我們也可以發現,現在方法趨向于越來越小巧——有時候僅僅只是幾行程式碼。
從本質上說,要想快速把握程式的目的和意義,只需要一定的程式碼就夠了。長方法不但令人難以接受,而且往往最終趨向於支離破碎。
其原因也非常簡單:長方法既難以理解,又難以維護,甚至還難以正常測試。
有一個相當不錯的測量方法可以衡量你的程式碼的複雜程度,以及出現 bug 的概率—— 迴圈複雜度。
該方法由 Thomas J. McCabe Sr 於 1976 年開發。迴圈複雜度使用方便簡單,能讓你在匆忙之中儘可能地保證程式碼執行正常。只需要數一數程式碼中‘if’語句和迴圈的數量,再加1,就是該方法的 CC 值。
當然這只是對程式碼執行路徑數量的粗略計數。不過,如果你的某個方法其迴圈複雜度值大於 10,我建議你重寫。
3. 過早的優化
這一點非常簡單。當我們在編寫程式碼的時候,有時我們會自作聰明地對某些程式碼過於注重細節過於精益求精,雖然看上去這些“明智”的程式碼比原先寫的那些提高了速度,但是你忽略了一個事實,這些“明智”的程式碼往往是難以閱讀難以理解的——而且真正節省的時間往往只有幾毫秒。這就是所謂的過早的優化。
著名的電腦科學家 Donald Knuth 曾經說過,“過早的優化是一切罪惡的根源”。
換言之就是:我們的程式碼需要清晰、乾淨,然後再重點找出真正的瓶頸並對其進行優化。千萬不要試圖過早的優化。
4. 使用全域性變數
話說回來,有的程式語言是完全沒有區域性變數這個概念的,所以不得不使用全域性變數。關於全域性變數,雖然我們可以在子函式中使用它,但是卻沒辦法宣告這一變數只能在該函式中使用。儘管如此,全域性變數依然非常受歡迎,因為我們只需宣告一次,即可到處使用,太省時省力了有木有。
但是它的優點也是它的缺陷,這也是關於全域性變數最糟糕的事情——我們沒有辦法控制它的改變,也沒辦法控制何時去訪問變數。假設某個全域性變數在呼叫到程式之前賦予了一個特定的值,但是很可能呼叫完了之後值就變了,而你卻毫無察覺。
5. 不進行評估
你的目標是寫一個應用程式,你鬥志昂揚,愈戰愈勇。但是突然間,你發現了效能問題和記憶體不足的問題。
進一步的調查表明,儘管你的設計對於現在這樣小型的使用者數量、記錄、條目執行良好,但是卻不適合大規模的情況——Twitter 就是例子。又或者它現在在你的 8GB RAM 和 SSD 的 3GHz PC 上執行順暢,但一旦到普通的 PC 上,它會比烏龜爬還要慢吞吞。
所以,部分設計程式還是需要評估,需要一系列的封底計算。有多少使用者需要同時處理多少個使用者?需要處理多少記錄?目標響應時間又是多少?等等。
儘量對這些型別的問題進行評估,這樣就可以對應用程式中的一些技術問題做一些更進一步的決策,如不同的演算法和快取。不要什麼亂七八糟的都納入到開發中去——你還需要好好評估目標和目的。
6. 大小差一錯誤(陣列邊界溢位)
這個錯誤基本上每一個程式設計師都犯過,通常在寫迴圈的時候,由於迴圈變數的步長增加過多或過少,導致迴圈遍歷元素的次數發生錯誤,產生陣列溢位的異常。
這個錯誤會導致遍歷陣列元素時訪問不存在的元素,或者遺漏應該遍歷的元素。產生這個錯誤的原因就是你忘記了陣列下標是從 0 開始還是從 1 開始了。
7. 淹沒異常
現在的程式語言大多使用異常系統作為錯誤報告技術,而不再是以往傳統的傳遞和檢查故障程式碼。現在的程式語言使用新的關鍵字來處理和捕獲異常,其名稱為 throw、try、finally 和 catch 等。
關於異常處理值得一提的是,它們的作用是展開堆疊,從巢狀程式自動返回,直到異常被捕獲並處理。不再需要你檢查錯誤條件,從而導致程式碼深陷錯誤測試的泥沼。
通過正確地運用異常處理,我們能夠使得軟體更為強大。比如說 catch 能讓我們捕獲異常,並根據異常型別執行某種行為。
關於異常處理,程式設計師犯的最大的錯誤有兩種。第一種是程式設計師對於他們 catch 的異常瞭解得不夠清楚具體。捕獲過於籠統化的異常型別可能會導致你在不經意間處理掉一些最好能夠保留的特定異常。而這樣做,可能會導致這些異常被淹沒,丟失。
第二個錯誤更為有害:程式設計師不想要任何異常離開自己的程式碼,因此捕獲之後忽略了它們。這就是所謂的空 catch 塊。他們可能是這樣想的,只要 throw 某些型別的異常就可以了:於是名正言順地忽略了這些異常。
而現實是,這可能會導致其他致命的執行時異常——如記憶體不足的異常,程式碼無效的異常等等,從而使得程式無法正常執行。因此,調整異常 catch 塊時應儘可能的具體化。
8. 純文字格式儲存密碼
資料安全性是永遠值得探討的話題,其重要性是不言而喻的。在這裡,我要鄭重告訴你的是,千萬不要將密碼用純文字格式儲存。
密碼的標準是,先儲存經過加密後雜亂無章的原始密碼,然後再輸入通過相同加密方法後的雜亂的密碼,看看它們是否匹配。
還不清楚這樣做的害處,那麼給你個提示:如果某個網站承諾,如果你忘記了原始密碼,他們會給你傳送電子郵件告訴你,那麼遠離這種網站。這可能會出現巨大的安全問題。假設有一天,該網站會被黑的話,那麼你所有的登入資訊都會被洩漏出去,而你除了忍氣吞聲惶惶而不可終日卻毫無辦法。所以,千萬不要接觸這類網站,同樣的,也不要在你的 app 裡用純文字的格式儲存密碼或其他的“祕密”。
9. 不驗證使用者輸入
以前的程式是單使用者的,於是我們對使用者輸入往往不以為然:畢竟,如果程式崩潰的話,只會影響到一個人的使用。我們的輸入驗證僅限於數值驗證、日期檢查,或其他型別的輸入驗證。
文字輸入往往不會特別驗證。不過後來出現了網頁。於是,你的程式有了遍佈世界的使用者。而一些惡意使用者則會通過輸入資料到你的程式,以試圖接管你的 app 和伺服器。
新型的攻擊大多是因為缺乏對使用者輸入的檢查。其中最著名的是 SQL 注入,通過標記注入,不好的使用者輸入可能會引發 XSS 攻擊(跨站指令碼)。
這兩種型別都依賴於使用者提供包含了 SQL 或者 HTML 片段的文字,來作為正常表單輸入的一部分。如果應用程式不驗證使用者輸入,直接就拿來用,那麼很可能就會執行篡改的 SQL,或者產生一些被攻擊的 HTML/JavaScript。
這反過來可能會使得 app 崩潰,或被黑客接管。為了避免這些情況,所以我們應該時時驗證或消除使用者輸入。
10. 不與時俱進
上述這些我總結的內容或許並不新鮮——你可能已經在其他的書籍或網頁上涉獵過。但是隨著時代的發展,會有越來越多的新的設計和程式設計技術面世。
而你如果還抱著一些陳舊的逐漸在被淘汰的技術不放,不願意學習和了解新的程式設計方法和技術——那麼你終將會被拍死在沙灘上。對於程式設計師,學習是永恆的課題。例如 TDD 和 BDD,SLAP 和 SOLID 方法,以及各種敏捷技術,都是我們應該學習的技術。
我們應該時刻保持對最新的程式設計藝術和實踐的同步。
英文原文:10 mistakes every programmer makes
譯文連結:http://www.geekwww.com/10-mistake-programmer.html
來自:部落格園
相關閱讀
評論(2)