糟糕的、差勁的,以及不該來當程式設計師的程式設計師

高博發表於2011-11-04

你是一個糟糕程式設計師的徵兆

缺乏根據程式碼推導的能力

根據程式碼推導意味著能夠跟蹤執行路徑(“在腦子裡執行程式”),並且明白程式碼的目標是什麼。

症狀表現

  1. 存在“莫名程式碼”,或是對程式目標毫無成效,但卻在拼命維護的程式碼(比如,初始化了卻從未使用的變數,呼叫了和目標無關的函式,產生了未被使用的輸出等等)
  2. 將等效函式多次執行(例如:多次呼叫save()函式,“只是為了確定真的儲存了”)
  3. 通過撰寫多餘程式碼覆蓋掉出錯程式碼的結果,來修復缺陷
  4. “車軲轆話程式碼”(yoyo code),即將一個值轉換成一種不同的表示,然後又把它轉換成原始的樣子。(例如:將一個十進位制數轉換成一個字串,又轉回一個十進位制數,或用空白填充一個字串,又對它做空白修剪操作)
  5. “推土機式程式碼”(bulldozer code),表面上看是把程式碼塊打散成若干子程式的重構動作,但是這些打散後的若干子程式卻完全不可能在另一種環境中複用(因為它們之間有很高的耦合,不能分開使用)

補救措施

為了克服這方面的不足,程式可以採用IDE自帶的偵錯程式作為輔助,如果該偵錯程式提供了單行步進能力的話。例如,在Visual Studio中,就可以在出問題的程式碼區塊的開頭設定一個斷點,並通過按“F11”鍵單行步進,並檢查變數值的前後變化——直到你理解程式碼是要做什麼事為止。如果目標環境中沒有偵錯程式這種功能特性,那就找一個有這種特性的來練手。

我們的目標是達到這麼一個狀態,你可以不再需要偵錯程式就可以在腦子裡來跟蹤程式碼走向,並且你有了足夠的耐心來根據程式狀態來思考程式碼在做什麼。回報是識別冗餘和無用程式碼的能力,以及從已有程式碼中發現缺陷,並且不必從頭重新實現一遍整套演算法。

未能透徹理解語言的程式設計模型

物件導向的程式設計是一個語言模型的例子,其他的還有函式式或宣告式程式它們中的每一個都與程式導向的或是命令式的程式設計有著顯著的不同,正如程式導向的程式設計與彙編或是基於GOTO的程式設計有著顯著的不同一樣。還有一些語言,它們從屬於一種主要的程式設計模型(比如物件導向的程式設計),但是同時也引入了一些它們提供的改進,比如列表解析、泛型、鴨型型別(譯註:即用相同的介面和大部分輸出響應來模擬某種型別,實際上有所不同的型別,用語取自諺語“如果它叫喚時像只鴨子、吃食時像只鴨子、連跛腳都像只鴨子,那它就是隻鴨子”)等等。

症狀表現

  1. 不擇手段地使用各種語法來打破當前使用的模型,爾後採用命令式/過程式風格來書寫餘下的程式
  2. (物件導向)試圖呼叫未例項化類中的非靜態函式和變數,並且難以理解為何通不過編譯(譯註:即分不清類和物件)
  3. (物件導向)寫一大堆“xxxxxManager”類,裡面包含所有操作類的方法,但這些類卻沒有自己的方法。(譯註:即仍然把類看作struct,未能掌握用類自己的方法——C++中叫做成員函式——來操作類資料的新模型)
  4. (關係型)將資料庫當作一個物件儲存,在客戶程式碼中完成所有的連線和關係模塑
  5. (函式式)為相同的演算法建立多個函式版本來操作不同型別和運算元,而非將高層抽象的函式傳遞給一個泛化的實現
  6. (函式式)手動快取確定性函式(譯註:即對於同樣輸入返回同樣結果的函式)的返回結果,即使平臺會自動完成這件事(例如SQL和Haskell)
  7. (純函式式)使用從別人程式裡複製-貼上的程式碼來處理與I/O和單子
  8. (宣告式)採用命令式程式碼逐個地設定變數的值,而不使用資料繫結

補救措施

如果你的技能缺乏是無效教學或研究的產物,那麼另一個老師就是編譯器本身。要學習一種新的程式設計模型,在效果方面無與倫比的方法就是啟動一個新工程並將自己投入在應用那些全新結構上,別管它們是什麼,也別管看上去傻不傻。你還需要練習使用你熟悉的一切來解釋該模型的特性,用語不妨粗糙一些,然後不斷重複地構造你的新詞彙表,直到你同樣掌握了新特性的精妙之處為止。例如:

  • 第1階段:“物件導向就是帶有方法的記錄型別”(譯註:記錄即C-style struct)
  • 第2階段:“物件導向中的方法,就是在一個小程式裡執行的函式,這個小程式帶有自己專屬的全域性變數”
  • 第3階段:“這些全域性變數被稱為域,其中有些帶有私有訪問層級,在這個小程式之外是看不到它們的”
  • 第4階段:“為元素搞私有和公有訪問層級的名堂,這個思想是要隱藏實現細節,並且只暴露一個乾淨的介面,即所謂封裝”
  • 第5階段:“封裝意味著我的業務邏輯不必被實現細節所汙染”

第5個階段看起來對所有語言都適用,因為它們的確是想要程式設計師理解到這一步,這樣他們就可以表述程式的目的,而不會將它埋藏在如何實現的具體做法中。再舉函式式程式設計作為另一例:

  • 第1階段:“函式式程式設計就是把確定性函式連結在一起”
  • 第2階段:“當所有的函式都成了確定性的,它們在要求輸入結果之前不必被執行,並且只須需要多少結果就執行多少部分即可。即所- 謂緩式評估求值和部分評估求值”
  • 第3階段:“為了支援緩式評估求值和部分評估求值,編譯器要求我以對單個引數做變換的形式撰寫函式,有時變換的結果是另一個函式。即所謂柯里化”
  • 第4階段:“當所有的函式都完成了柯里化後,編譯器就可以運用一個約束求解器來決定最佳執行計劃了”
  • 第5階段:“通過讓一個約束求解器來決定機器細節,我撰寫程式時就可以只描述我想要什麼結果,而非怎樣得到結果了”

原文連結:http://badprogrammer.infogami.com/

本文參加 Translate Geeks to Chinese 翻譯活動

相關文章