1. 程式人生 > >微軟資深軟體工程師:閱讀程式碼真的很難

微軟資深軟體工程師:閱讀程式碼真的很難

編者按:原文作者 Eric Lippert 是微軟一名資深軟體設計工程師,從1996年起一直在微軟開發部門任職,協助設計並實現VBScript、JScript、JScript .NET、Windows Script Host、Visual Studio Tools for Office 和 C#。

Escalation的工程師JeremyK在他部落格中問到:

你是怎麼教人們快速深入挖掘不熟悉的程式碼(不是自己所寫的)?我學習如何程式設計的方法很傳統 —— 自己動手編碼。但我現在很糾結:到底是集中精神閱讀原始碼,還是自己編寫。對我而言,似乎唯一有效的方法就是自己寫過。

不是和Jeremy開玩笑,寫程式碼的確沒有讀程式碼難。

首先,我同意你的看法,幾乎很少有人能讀程式碼但不會寫程式碼。這不像自然書面語或口語,理解他人的意思並不需要去理解他們為什麼要那樣說。比如,如果我說:

“寫程式碼有兩種方式:一種嚴格且詳細,另一種模糊且草率。前者生成簡潔分層的婚禮蛋糕,後者卻是義大利麵條。”

上面這句話產生一個平衡且幽默的效果,但即使聽眾和讀者不理解我使用“零照應”和“並列句”這樣的文字技巧,也會理解我要說的意思。但是說到程式碼,既要從程式碼本身中理解程式碼作者的意圖,又要理解程式碼產生的預計效果,這兩者都極為重要。

Eric Lippert

Eric Lippert

因此,我又回到那個問題了,有些人需要快速切入程式碼,但不需要動手寫程式碼,那我們如何編寫適合這些人的程式碼?

下面是我在編寫程式碼時,盡力去做的事,目的就是使其他人能輕鬆閱讀:

1. 使程式碼遵從工具。Object Browsers和Intellisense雖然很好,但我告訴你,我是守舊派。如果找不到我想要的,我會不高興。什麼使得程式碼成為可查詢的呢?

●像”i”這樣的變數名不好。如果沒有明確的錯誤提示,你就無法輕易查詢程式碼。

●避免使用是其他名字字首的名字。比如,在程式碼中有個“perfExecuteManifest”,如果再有一個“perfExecuteManifestInitialize”,這就會讓我抓狂,因為每次在原始碼中查詢前者時,我不得不費力地過濾掉後者所有的例項。

●“臨時傳遞資料”(tramp data)應使用相同的名字。所謂“臨時傳遞資料”(tramp data),就是指那些傳遞給方法A的變數,還要傳給方法B的變數。這兩類變數實際上是相同的,所以如果它們有著相同的名字,則更好。

●別用巨集來重新命名東西。如果有個方法叫get_MousePosition,那別這樣GETTER(MousePosition)來宣告該方法。因為我會找不到實際的方法名。

●Shadowing會引起很多問題,請不要用它。

2. 堅持使用一種命名模式。如果你打算用匈牙利命名法,那就堅持並廣泛使用,否則將適得其反。使用匈牙利命名法來記錄資料,而不是儲存型別;記錄普遍事實,而不是臨時條件。

3. 使用斷言來記錄先決條件(preconditions)和後置條件(postconditions)。

4. 別縮寫英文單詞。確切來說,別縮寫成稀奇古怪的形式。在指令碼引擎中,有個變數名叫“NME”,這讓我非常抓狂!它應當叫“VariableName”。

5. C語言標準執行時庫的設計不是很優秀。別去效仿它。

6. 別寫“聰明”的程式碼;當代碼出現問題,維護程式碼的程式設計師沒時間去理解你的聰慧。(編注:這條建議即為:編寫可維護的程式碼,詳情可參見《明星軟體工程師的10種特質》中的第8點。)

7. 理解程式語言特性的設計初衷,使用這些特性去做它們適合完成的工作,而不是它們能做到的工作。例如:別把異常當做一般的流控制機制來使用(即便你能做到),而應該用它們來報告錯誤。彆強制把介面指標轉換成類指標,即便你知道這樣沒問題。

8. 按功能單元劃分原始碼樹,而不是按組織結構。比如:我目前所在團隊中,有2個根子目錄的名字是“Frameworks”和“Integration”,這是兩個團隊的名字。不巧的是,Frameworks團隊名下有一個叫“Adaptor”的子目錄,而“Adaptor”卻是Integration的子目錄,這就非常令人迷惑。同理,(如果)有著相同子目錄的不同的子樹,有些是客戶端的元件,有些是服務端的元件;有些是管理元件,有些是非管理元件;有些是處理型元件,有些是非處理型元件;有些是零售元件,有些內部測試工具。這就會亂七八糟的。

當然,我實際上根本沒有回答Jeremy的問題——如何除錯不是我寫的程式碼?

這取決於我的目的。如果我只是因為一個bug,而深挖一段具體的程式碼,我會在偵錯程式中逐步跟蹤所有程式碼,寫下我“走過”的呼叫分支,記錄下哪些方法是特定資料結構的“生產者”,哪些方法是“消費者”;我也會仔細盯著輸出視窗,查看出現的有用資訊;還要開啟異常捕捉器,因為異常通常是問題所在。設定斷點;我會記錄所有和我上面建議相反的地方,因為這些東西很可能誤導了我。

如果我想在理解一段程式碼後修改它,我通常是從程式碼頭部開始,或者先查詢公共方法。我要知道類是如何實現的,它是如何擴充套件的,它的作用,它是如何嵌入整個程式碼中的?我會盡力理解這些東西后,才去瞭解這些特定部分(程式碼)是如何實現的。這耗時雖更長些,但如果你準備改動複雜程式碼,你應當那樣做。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式