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

來源:互聯網
上載者:User

標籤:style   blog   http   io   os   使用   ar   strong   for   

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

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

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

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

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

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

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

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

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

  • 使代碼遵從工具。Object Browsers和Intellisense雖然很好,但我告訴你,我是守舊派。如果找不到我想要的,我會不高興。什麼使得代碼成為可查詢的呢?
    • 像"i"這樣的變數名不好。如果沒有明確的錯誤提示,你就無法輕易尋找代碼。
    • 避免使用是其他名字首碼的名字。比如,在代碼中有個“perfExecuteManifest”,如果再有一個“perfExecuteManifestInitialize”,這就會讓我抓狂,因為每次在源碼中查詢前者時,我不得不費力地過濾掉後者所有的執行個體。
    • “臨時傳遞資料”(tramp data)應使用相同的名字。所謂“臨時傳遞資料”(tramp data),就是指那些傳遞給方法A的變數,還要傳給方法B的變數。這兩類變數實際上是相同的,所以如果它們有著相同的名字,則更好。
    • 別用宏來重新命名東西。如果有個方法叫get_MousePosition,那別這樣GETTER(MousePosition)來聲明該方法。因為我會找不到實際的方法名。
    • Shadowing會引起很多問題,請不要用它。
  • 堅持使用一種命名模式。如果你打算用匈牙利命名法,那就堅持並廣泛使用,否則將適得其反。使用匈牙利命名法來記錄資料,而不是儲存類型;記錄普遍事實,而不是臨時條件。
  • 使用斷言來記錄先決條件(preconditions)和後置條件(postconditions)。
  • 別縮寫英文單詞。確切來說,別縮寫成稀奇古怪的形式。在指令碼引擎中,有個變數名叫“NME”,這讓我非常抓狂!它應當叫“VariableName”。
  • C語言標準執行階段程式庫的設計不是很優秀。別去效仿它。
  • 別寫“聰明”的代碼;當代碼出現問題,維護代碼的程式員沒時間去理解你的聰慧。
  • 理解程式設計語言特性的設計初衷,使用這些特性去做它們適合完成的工作,而不是它們能做到的工作。例如:別把異常當做一般的流量控制機制來使用(即便你能做到),而應該用它們來報告錯誤。彆強制把介面指標轉換成類指標,即便你知道這樣沒問題。
  • 按功能單元劃分源碼樹,而不是按組織圖。比如:我目前所在團隊中,有2個根子目錄的名字是“Frameworks”和 “Integration”,這是兩個團隊的名字。不巧的是,Frameworks團隊名下有一個叫“Adaptor”的子目錄,而“Adaptor”卻是Integration的子目錄,這就非常令人迷惑。同理,(如果)有著相同子目錄的不同的子樹,有些是用戶端的組件,有些是服務端的組件;有些是管理組件,有些是非管理組件;有些是處理型組件,有些是非處理型組件;有些是零售組件,有些自我裝載工具。這就會亂七八糟的。

當然,我實際上根本沒有回答Jeremy的問題——如何調試不是我寫的代碼?

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

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

如何閱讀代碼?像某些人一樣……

我已經記不清有多少次看到程式員(用滑鼠)滾上滾下地看著不熟悉的代碼,幾分鐘過後,他們的臉上浮現出不悅的表情。他們不久後會宣告說,那代碼不值一讀,為什麼要浪費時間呢?我們只能用其他方法解決問題。我不確定(他們)在期待什麼,是通過潛移默化來吸收代碼的含義,還是集中精神盯著代碼來得到啟發?你不能只靠長時間盯著代碼來閱讀代碼,你要理解它並化為己用。 這裡有一些我喜歡用的技巧,雖然這不是一份詳盡的列表,但我發現其中有些特別有用。

  • 1. 儘力構建並運行代碼。這通常是一個簡單的步驟,就像你在看可啟動並執行代碼(這和隨機代碼相反)。不過,並非總是如此。通過構建和執行代碼,你能從中學到很多上層代碼結構。說到工作代碼,你是否非常熟悉如何構建你的當前項目?雖然構建通常非常複雜,但通過構建並產生可執行檔代碼,你能學到很多。
  • 2. 不要只注重細節。你要做的第一件事是,在你正閱讀的代碼中,找到代碼結構和風格。首先瀏覽一下代碼,儘力理解不同程式碼片段要做什麼。這會讓你熟整個代碼的上層結構,你也能領會到你正處理的代碼的一些構思(良好架構和意大利麵條等)。這時候,你可以找到切入點(不管它是什麼,主函數、servlet 或控制器等),並查看代碼如何在那裡分支。不要在這上面花過多的時間,隨著你愈加熟悉代碼,你可以隨時回來查看。
  • 3. 確信自己理解所有結構。除非你碰巧是所用程式設計語言的首席專家,否則該語言有些它能做的事你可能還不知道。當你在瀏覽代碼時,記下所有你或許不熟悉的結構。如果有很多不熟悉的結構,你要做的下一步非常明顯。如果你不知道代碼要做什麼,那你就走不了很遠。 即便只有幾個你不熟悉的結構,你應當深入查看。你現在是在探索你所用程式設計語言中你以前不知道的東西,為此花幾個小時來閱讀代碼,我也非常樂意。
  • 4. 既然你對大多數結構已有很好瞭解,那現在是該做些隨機深入研究了。 就像步驟2,開始瀏覽代碼,當這次要挑選一些隨機函數或類,並開始逐行詳細查看。這是硬仗開始的地方,但也是你要取得主要成功的地方。這裡的構想,會形成你正在查看的程式碼程式庫的思維模式。也不要在這上面花過長的時間,但在繼續前行之前,你要儘力並極大吸收一些有內容的代碼塊。這個步驟,你也可以隨時反覆回過頭來,每次你都會瞭解更多的背景,並收穫更多。
  • 5. 毫無疑問,在前面這些步驟中,肯定有你困惑的地方,所以這是你做些測試的最佳時間。在測試的時候,你的麻煩可能會更少,同時你也能理解代碼。 我一直感到奇怪,開發人員忽略一套寫得很好很全面的測試代碼,而儘力去閱讀並理解某些代碼。當然了,有時候並沒有測試。
  • 6. 如果你說沒有測試,那這聽起來是編寫測試的時候了。 (編寫測試)有很多益處,有助於你自己的理解,有助於你提升程式碼程式庫,閱讀代碼時也能編寫代碼,這是該你出手做些事的時候。 即便已經有了測試,通常你也可以編寫一些測試,你總能受益的。 測試代碼通常需要換種方式思考問題,那些你以前不太明了的概念也會變得更清晰。
  • 7. 提取奇特的代碼,使其成為單獨的程式。我發現閱讀代碼是個非常有趣的練習,即便只為節奏變化。即便你不瞭解代碼的底層細節,你或許能知道一些代碼在上層結構上要做什麼。為什麼不提取一些特定的函數,單獨列為獨立的程式。當你在執行小段程式時,調試也會更簡單。反過來說,可能還需要一些額外的步驟,才能理解你正查看的代碼。
  • 8. 代碼不乾淨?有異味? 為什麼不重構它?我並不建議你重寫整個程式碼程式庫,但重構部分代碼,真的有助於你的理解層次上升一層。 把你理解的函數拿出來,改成獨立的函數。在你知道之前,原來的大函數看起來易管理,你可以在腦海中修改它。 重構允許你把代碼變成自己的,無需完成重寫代碼。如果有好的測試,有助於重構,但即便你沒有好的測試,抽取你確定的函數並做測試。即便測試看起來完全不充分,但作為一個開發人員,你得學著相信你的技能,有時候你只需努力去做(重構)。(如果你必須重構,你通常都可以把代碼恢複原狀。)
  • 9. 如果沒什麼能幫上忙,那你就找個閱讀代碼的同伴。或許並非只有你一個人能從這代碼中獲益,所以去找一個人,一起閱讀代碼吧。 但你別找專家,他們會從上層結構上,向你解釋所有東西,你會錯失那些你自己詳細查看代碼時所能學到的細微差別。然而,如果不見效的話,你也不能理解,有時候,你能做的最好的事就是去問。向你的同事請教,如果你正在閱讀開原始碼,可以在互連網上找人問問。但是你要記住,這是最後一步,而不是第一步。

如果我時間緊迫,需要快速合理地理解某些代碼,並且我只能挑選上述步驟的其中一個,那我會選擇“重構”(即:第 8 個步驟)。雖然你能理解的東西不會很多,但那些你領會的東西,你會牢牢記住的。總之,有件事你需要記在心裡。如果你新接觸一個重要的程式碼程式庫,你不可能立即能理解它。這需要數天、數周和數月的潛心努力,接受這個事實。即便有一位專家和你在一起,也不能明顯地縮短時間。然而,當涉及到程式碼程式庫時,如果你能耐心並有條不紊地閱讀(和編寫)代碼,你最終能熟悉項目的方方面面,你能成為大牛。你或者是逃避閱讀代碼,經常尋求某人幫你講解某事。我知道我會成為哪一種人。

尋找閱讀代碼的機遇 – 不要錯失

我們喜歡編寫新代碼,是因為我們這次能正確處理問題。好吧,也許不是這次,但一定是下次。事實上是,你經常改進你的技術,但你從沒有恰當地處理問題。這就是編寫新代碼的價值所在,你可以曆練並磨練你的技能,但閱讀和把玩其他人編寫的代碼,(如果沒有更多的價值,)也是有同樣多的價值。你不僅能從中獲得一些有價值的技術知識,也能收穫領域知識,領域知識通常仍具更多價值(畢竟,代碼是文檔的最終形式)。

即便代碼寫得很神秘,無任何慣例可言,但還是有價值。你知道我在說的代碼,它幾乎看起來晦澀難懂,但不是有意而為之(因某些原因,Perl 語言代碼通常是這樣的)。不管什麼時候我看到那樣的代碼,我都會這樣想: 把它想象成只有你破譯它後才能學到的東西。是的,這是主要的痛楚之處,但要接受它,有時候你自己也會因瑣碎的原因而寫出那種使人困惑的代碼(否認沒有用,你知道這是真的)。好了,如果你花些時間來閱讀那樣的代碼,你更有可能最終寫出同樣的代碼。並不說你將會寫出那樣的代碼,但你有能力寫出那樣的代碼。最後,態度通常是最重要的(編註:態度決定一切)。如果你視閱讀代碼為日常繁瑣的工作,那它就是(繁瑣的工作),並且你會逃避,但如果你視其為一個機遇,那好事終將到來。

 

註:本文轉載自http://blog.csdn.net/sdulibh/article/details/38412955

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.