軟件設計的哲學:第二十章 爲什麼要寫註釋

代碼內文檔在軟件設計中起着至關重要的作用。 註釋對於幫助開發人員理解系統和有效地工作是必不可少的,但是註釋的作用遠遠不止於此。文檔在抽象中也扮演着重要的角色;沒有註釋,就無法隱藏複雜性。最後,編寫註釋的過程如果處理正確,實際上將改進系統的設計。相反,如果沒有良好的文檔記錄,好的軟件設計就會失去很多價值。

不幸的是,這一觀點並沒有得到普遍認同。相當一部分產品代碼基本上不包含註釋。許多開發人員認爲註釋是浪費時間;另一些人看到了註釋的價值,但不知何故卻從來沒有寫過。幸運的是,許多開發團隊認識到了文檔的價值,並且感覺這些團隊的流行程度正在逐漸增加。然而,即使是在鼓勵文檔化的團隊中,註釋也常常被看作是苦工的工作,許多開發人員不知道如何編寫註釋,因此生成的文檔通常是平庸的。不充分的文檔會對軟件開發造成巨大且不必要的拖累。

在本章中,我將討論開發人員用來避免編寫註釋的藉口,以及註釋真正重要的原因。第13章將描述如何寫出好的註釋,接下來的幾章將討論相關的問題,比如選擇變量名以及如何使用文檔來改進系統的設計。我希望這些章節能讓你 相信三件事:好的註釋可以使軟件的整體質量有很大的不同;寫出好的註釋並不難;而且(這可能很難相信)寫註釋其實很有趣。

當開發人員不寫註釋時,他們通常會用以下一個或多個理由來解釋他們的行爲:

  • 好的代碼是自我解釋的。
  • 我沒時間寫註釋。
  • 註釋過時了,會產生誤導。
  • 我所看到的註釋都是毫無價值的,何苦呢?

在下面的章節中,我將依次解釋這些藉口。

12.1 好代碼是自我解釋的

有些人認爲,如果代碼寫得好,那麼顯然不需要註釋。這是一個美味的神話,就像冰淇淋有益健康的謠言:我們真的願意相信它。不幸的是,事實並非如此。當然,在編寫代碼時可以做一些事情來減少註釋的需要,比如選擇合適的變量名(參見第14章)。儘管如此,仍然有大量的設計信息不能用代碼表示。例如,只能在代碼中正式指定類接口的一小部分,如其方法的簽名。接口的非正式方面,例如每個方法的高級描述或其結果的含義,只能在註釋中進行描述。還有許多其他的例子無法在代碼中描述,比如特定設計決策的基本原理,或者調用特定方法的條件。

一些開發人員認爲,如果其他人想知道一個方法做什麼,他們應該只閱讀方法的代碼:這將比任何註釋更準確。讀者可以通過閱讀代碼來推斷方法的抽象接口,但這將是耗時且痛苦的。此外,如果您編寫代碼時期望用戶能夠讀取方法實現,那麼您將嘗試使每個方法儘可能短,以便於讀取。如果該方法做了任何重要的事情,您將把它分解成幾個更小的方法。這將導致大量的淺方法。而且,它並沒有真正使代碼更容易閱讀:爲了理解頂級方法的行爲,讀者可能需要理解嵌套方法的行爲。對於大型系統,用戶通過閱讀代碼來學習行爲是不現實的。

此外,註釋是抽象的基礎。回顧第4章,抽象的目標是隱藏複雜性:抽象是實體的簡化視圖,它保留了基本信息,但忽略了可以安全忽略的細節。如果用戶必須閱讀方法的代碼才能使用它,那麼就沒有抽象:方法的所有複雜性都暴露出來了。如果沒有註釋,方法的惟一抽象就是它的聲明,它指定了方法的名稱、參數和結果的名稱和類型。聲明缺少太多的基本信息,無法提供一個有用的抽象。例如,提取子字符串的方法可能有兩個參數,start和end,表示要提取的字符的範圍。僅從聲明中無法判斷提取的子字符串是否包含end所指示的字符,或者如果開始>結束會發生什麼。註釋允許我們捕獲調用者需要的附加信息,從而在隱藏實現細節的同時完成簡化的視圖。同樣重要的是,註釋是用英語等人類語言寫的;這使得它們不如代碼精確,但它提供了更強的表達能力,因此我們可以創建簡單、直觀的描述。如果您想使用抽象來隱藏複雜性,註釋是必不可少的。

12.2 我沒有時間寫註釋

人們傾向於優先考慮比其他開發任務更低的註釋。如果要在添加新特性和記錄現有特性之間進行選擇,那麼選擇新特性似乎是合乎邏輯的。然而,軟件項目幾乎總是處於時間壓力之下,而且總是有一些事情看起來比寫註釋更重要。因此,如果您允許取消文檔的優先級,那麼最終將沒有文檔。

這個藉口的反面論據是第15頁所討論的投資心態。如果您想要一個乾淨的軟件結構,它將允許您在長期內高效地工作,那麼您必須預先花一些額外的時間來創建這個結構。好的註釋會對軟件的可維護性產生巨大的影響,因此花在註釋上的精力很快就會得到回報。此外,寫註釋不需要花很多時間。問問你自己你花了多少開發時間來打字(相對於設計、編譯、測試等等),假設你沒有任何註釋;我懷疑答案是否超過10%。現在假設您輸入註釋的時間與輸入代碼的時間一樣多;這應該是一個安全的上限。根據這些假設,寫出好的註釋不會增加超過10%的開發時間。擁有良好文檔的好處將很快抵消這一成本。

此外,許多最重要的註釋都與抽象相關,比如類和方法的頂級文檔。第15章將討論這些註釋應該作爲設計過程的一部分來編寫,而編寫文檔的行爲可以作爲改進整體設計的重要設計工具。這些註釋立竿見影。

12.3 註釋會過時併產生誤導

註釋有時確實會過時,但這在實踐中不一定是主要問題。保持文檔更新不需要付出巨大的努力。只有在對代碼進行了較大更改的情況下,才需要對文檔進行較大的更改,並且代碼更改將比文檔更改花費更多的時間。第16章討論瞭如何組織文檔,以便在代碼修改後儘可能輕鬆地更新文檔(關鍵思想是避免重複的文檔,並使文檔與相應的代碼保持一致)。代碼審查爲檢測和修復陳舊的註釋提供了一種很好的機制。

12.4 我所看到的一切註釋都是毫無價值的

在這四個藉口中,這可能是最有價值的一個。每個軟件開發人員都看到過沒有提供任何有用信息的註釋,而且大多數現有文檔充其量也就是一般。幸運的是,這個問題是可以解決的;編寫可靠的文檔並不難,只要您知道如何做到這一點。下一章將爲如何編寫良好的文檔並隨時間進行維護提供一個框架。

12.5 良好的註釋的好處

既然我已經討論了(希望已經揭穿了)反對寫註釋的爭論,讓我們考慮一下從好的註釋中可以得到的好處。註釋背後的總體思想是捕獲設計者頭腦中但是不能在代碼中表示的信息。這些信息的範圍很廣,從底層的細節(比如驅動一段特別複雜的代碼的硬件怪癖),到高層的概念(比如類的基本原理)。當其他開發人員稍後進行修改時,註釋將允許他們更快更準確地工作。沒有文檔,未來的開發人員將不得不重新推導或猜測開發人員的原始知識;這將花費額外的時間,並且如果新開發人員誤解了原始設計人員的意圖,就會有出現bug的風險。即使是最初的設計人員做出了更改,註釋也是很有價值的:如果距離您最後一次使用代碼已經超過幾個星期了,那麼您就會忘記最初設計的許多細節。

第二章描述了複雜性在軟件系統中表現出來的三種方式:

  • 變更放大:一個看似簡單的變更需要在很多地方修改代碼。
  • 認知負荷:爲了做出改變,開發人員必須積累大量的信息。
  • 未知的未知:不清楚需要修改哪些代碼,或者爲了進行這些修改必須考慮哪些信息。

好的文檔有助於解決最後兩個問題。通過向開發人員提供他們需要更改的信息,並使開發人員很容易忽略不相關的信息,文檔可以減少認知負荷。如果沒有足夠的文檔,開發人員可能不得不閱讀大量的代碼來重構設計者的想法。文檔還可以通過澄清系統的結構來減少未知的未知,這樣就可以清楚哪些信息和代碼與任何給定的更改相關。

第二章指出了複雜性的主要原因是依賴性和模糊性。好的文檔可以澄清依賴關係,並填補空白以消除模糊性。

接下來的幾章將向您展示如何編寫好的文檔。他們還將討論如何將文檔編寫集成到設計過程中,從而改進軟件的設計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章