低级程序员才喜欢写注释!
作者 | Tameem Iftikhar
譯者 | 平川
策劃 | Tina
我并不是提倡不寫代碼注釋,只是建議不要過于依賴注釋,這樣可以使代碼更干凈、更有表現力,這也能提高開發人員的水平。我自己也在尋求編寫更簡潔的代碼,我盡力不編寫糟糕的注釋,并在可能時重構代碼。
本文最初發布于 Level Up Coding 官方博客,經原作者授權由 InfoQ 中文站翻譯并分享。
這篇文章的標題可能會讓你情緒激動,但請先耐心聽我說完。在適當的位置寫下適當的注釋可能非常有用,但是沒有什么比無用的注釋更讓代碼混亂了。在某些情況下,我敢說,注釋可以彌補我們在代碼中沒有完全表達出來的意思。因此,寫注釋不值得贊美,而是應該停下來問問自己,是否有更好的方式可以用代碼來表達自己。
帶有少量注釋的清晰而富于表現力的代碼,要比帶有大量注釋的混亂而復雜的代碼好得多。如果你已經把代碼弄得一團糟,不要花時間寫注釋來解釋,而是要花時間梳理代碼。如果每次寫注釋的時候,你都冥思苦想,覺得自己的表達能力不足,那么最終你就會寫出簡潔明了的代碼,完全沒有必要寫注釋。鼓勵自己用代碼表達。
為什么對注釋如此不屑?
因為它們會說謊,還會把代碼弄得亂七八糟。雖說并非總是如此,也并非有意如此,但卻經常如此。糟糕的代碼和帶有大量注釋的代碼之間有很高的相關性。注釋存在的時間越久就越容易偏離它們所描述的代碼——在某些情況下,它們可能是完全錯誤的。實際上,隨著代碼庫和團隊的增長,維護注釋成了不可能的事情。
注釋不同于《辛德勒的名單》。它們不是“純善的”。事實上,注釋充其量是一種必要的惡。——Robert C.Martin
當談論關于注釋的話題時,很重要的一點是,我們要看一下什么是恰當的注釋,什么是糟糕的注釋,這樣我們才能學會寫更好的注釋,或者完全避免注釋。
?
恰當的注釋
并不是所有的注釋都是不好的——有些注釋實際上非常必要。
??
出于法律目的的注釋
有時候,你可能需要出于法律目的編寫特定的注釋,比如開源項目的創作許可。一些現代化的 IDE 和文本編輯器會自動將它們折疊起來,保持工作區的整潔。
?
魔術表達式
如果你有一個復雜的 SQL 或正則表達式,它以神奇的方式做了一些令人興奮的事,那么請務必注釋,以便讓讀者更容易理解,因為我們都不是 Regex 忍者。
// 匹配電子郵件地址的正則表達式var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;return re.test(String(email).toLowerCase());// 注意:添加一個富于表現力的函數名,注釋就變得沒有必要了function validateEmail(email) {var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;return re.test(String(email).toLowerCase());}?
說明意圖
在某些情況下,注釋有助于解釋決策或特定解決方案背后的意圖。例如,測試套件中的一條注釋告訴我們添加這行代碼是為了降低死鎖的幾率。
for x in range(1, 500): # 這是運行多個并行測試時預防死鎖的最好方法 time.sleep(0.5) runTest(x)?
結果預警
用注釋說明代碼可能會產生嚴重的或可怕的后果,甚至鼓勵這樣做。在本例中,開發人員讓讀者知道,當與回調函數一起使用時,QT 函數不是線程安全的。一般來說,如果一條注釋可以避免某個人在編程時陷入絕望,那么它就是有用的。
""" 許多 Qt 函數都不是線程安全的。如果你使用回調函數, 即使你在所有繪制調用代碼的周圍都加上鎖,你也會遇到段錯誤, 因為 Qt 的主事件循環仍在運行,并且使用了沒加鎖的資源。"""from multiprocessing.pool import ThreadPoolimport sysfrom threading import Lockimport timefrom PyQt5 import QtCore, QtWidgetsclass Task(QtCore.QObject): updated = QtCore.pyqtSignal(int, int) ............... ...............?
TODO 注釋
這些注釋可以幫助我們標記那些我們認為應該做,但是由于某些原因沒有做到的事情。它可能會提醒你刪除廢棄的特性,或者請求其他人查看某個問題。它可能是要求其他人想一個更好的名字,或者是提醒他們根據計劃事件做出修改。
請記住,TODO 注釋不是在系統中留下糟糕代碼的借口。本質上,每一行代碼都是一種負擔——最安全、最快的代碼是根本沒有代碼。
現在,大多數優秀的 IDE 都提供了特殊的指令和特性來定位所有的 TODO 注釋,所以不太可能漏掉它們。盡管如此,你也不希望代碼中到處都是 TODO。所以要經常瀏覽一下,刪除那些你能刪除的。
?
糟糕的注釋
這個清單比較長,但在本節中,我們將看到一些更為老生常談而又隨處可見的注釋。
??
明知故問的注釋
有些注釋的意思顯而易見,即它們沒有增加任何實際的價值,而且大多是噪音。
下面是一個開源項目的代碼片段,其中包含大量明知故問型的注釋,這些注釋使代碼變得混亂而晦澀。它們所提供的信息并不比代碼本身多,而且在某些情況下,閱讀注釋的時間甚至比閱讀代碼長。
/*** 與該容器相關的集群*/protected Cluster cluster = null;/*** 人類可讀的容器名*/protected String name = null;/*** 該容器的父容器*/protected Container parent = null;/*** 創建一個 Loader 配置父類加載器*/protected ClassLoader parentClassLoader = null;?
不清不楚的注釋
如果你寫注釋是為了符合公司規定,或者你只是覺得有必要添加一些注釋,那么你在注釋時就不會進行適當的思考。所以,如果你真的寫了一條注釋,花點時間讓它對閱讀它的人有所幫助。
def load_config():try: do_useful_stuff()except Exception as ex:# 如有異常,退回到默認狀態。在這個例子中,作者想要傳達一些有關異常情況的重要信息。但這條注釋沒能解釋清楚我們將退回到什么樣的默認狀態。如果一條注釋要求我們轉到另一個模塊來找出默認值,那么它就沒有發揮應有的作用。
?
注釋掉代碼
在團隊準備好刪除代碼之前先將其注釋掉似乎是一個好主意,但是不要這樣做。注釋代碼是一種弊端,團隊中的其他成員不會刪除它,因為他們會認為它很重要。我們不是都在使用源碼控制嗎?所以我們不需要保留舊的代碼。我們可以跳到任何我們想要的版本。
?
噪音注釋
有些注釋毫無意義,純粹是噪音。時間久了,我們的大腦就會走馬觀花,我們也會開始跳過那些需要注意的重要注釋。考慮一下下面的例子,其中的注釋提供了很多價值嗎?
-----------------------------# Exhibit A# 默認構造函數def get_todays_date():return date.today()-----------------------------# Exhibit B# 返回月份的天# @return: 月份的天def get_day_of_month()return day_of_month用編寫干凈代碼的決心取代制造噪音的誘惑,你將成為一個更好、更快樂的程序員。
?
強制性注釋
這肯定會引起爭議。如果規定每個函數都需要一個 Java 文檔或 Python docstring,是不是有點傻?大多數時候,類或函數名已經告訴我們注釋所描述的內容,它們是多余的。在這個例子中,注釋的數量比代碼的數量還多——這讓我很惱火。
class ComplexNumber:""" 這是一個用于復數的數學運算類。屬性: real (int):復數的實部。 imag (int):復數的虛部。 """ def __init__(self, real, imag):""" ComplexNumber 類的構造函數。參數: real (int):復數的實部。 imag (int):復數的虛部。 """ def add(self, num):""" 該函數用于復數求和。參數: num (ComplexNumber):要加的復數。返回值: ComplexNumber:包含和的復數。 """re = self.real + num.real im = self.imag + num.imag return ComplexNumber(re, im) help(ComplexNumber) # 訪問類的 docstringhelp(ComplexNumber.add) # 訪問方法的 docstring?
使用好的函數名或變量名
你可以使用更具表達性的函數和變量名替換注釋,從而使代碼更簡潔。考慮下面的例子,第一個例子中的注釋就變得沒有必要了,因為有一個更好的函數名可以準確地告訴讀者這個函數做了什么。
# 檢查日期是否是過去的日期def check_date(date):if date < today:return truereturn false def is_past_date(date):if date < today:return truereturn false?
注釋不能彌補代碼的糟糕
編寫注釋有一個比較常見的原因是糟糕的代碼。我們以前都見過這種情況,在某種程度上,我們自己也犯過這樣的錯誤。我們寫一個模塊或類,我們心里知道它混亂而無序。我們知道它一團糟。所以我們對自己說,“哦,我最好加下注釋!”不!你最好把代碼梳理清楚!
/*這段代碼糟透了。我知道,你知道,每個人都知道。我們假裝什么都沒發生,然后繼續前進。以后你叫我白癡好了。*/?
小? ? 結
我并不是提倡不寫代碼注釋,只是建議不要過于依賴注釋,這樣可以使代碼更干凈、更有表現力,這也能提高開發人員的水平。我自己也在尋求編寫更簡潔的代碼,我盡力不編寫糟糕的注釋,并在可能時重構代碼——將我的代碼從宜家的一幅畫變成梵高的作品。
所以讓我們約法三章,不要寫這么多注釋。
延伸閱讀:
https://levelup.gitconnected.com/every-time-you-comment-code-youve-already-failed-6fa9773b080f
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的低级程序员才喜欢写注释!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LuoguP33294123】[ZJO
- 下一篇: 【BZOJ5469】[FJOI2018]