自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Redis是如何寫(xiě)代碼注釋的?

數(shù)據(jù)庫(kù) 其他數(shù)據(jù)庫(kù) Redis
我的工作始于隨機(jī)地閱讀Redis源代碼,以檢查注釋是否以及為什么在不同的上下文中起作用。我很快發(fā)現(xiàn),注釋的作用來(lái)源于多方面:它們?cè)诠δ?,編程風(fēng)格,長(zhǎng)度和更新頻率方面往往非常不同。

Redis是如何寫(xiě)代碼注釋的?

許多人認(rèn)為,如果代碼寫(xiě)得足夠扎實(shí),注釋就沒(méi)什么用了。在他們看來(lái),當(dāng)一切都設(shè)計(jì)妥當(dāng)時(shí),代碼本身會(huì)記錄其作用,因此代碼注釋是多余的。我對(duì)此持不同意見(jiàn),主要出于兩個(gè)原因:

1、許多注釋并未起到解釋代碼的作用。

2、注釋使讀者不必憑空想象太多細(xì)枝末節(jié),幫助讀者降低認(rèn)知負(fù)擔(dān)。

注釋的分類(lèi)

我的工作始于隨機(jī)地閱讀Redis源代碼,以檢查注釋是否以及為什么在不同的上下文中起作用。我很快發(fā)現(xiàn),注釋的作用來(lái)源于多方面:它們?cè)诠δ?,編程風(fēng)格,長(zhǎng)度和更新頻率方面往往非常不同。我最終轉(zhuǎn)向了注釋分類(lèi)。

在研究期間,我確定了九種注釋類(lèi)別:

* 函數(shù)注釋 Function comments

* 設(shè)計(jì)注釋 Design comments

* 原因注釋 Why comments

* 教學(xué)注釋 Teacher comments

* 清單注釋 Checklist comments

* 引導(dǎo)注釋 Guide comments

* 瑣碎注釋 Trivial comments

* (代碼)負(fù)債注釋 Debt comments

* 備份注釋 Backup comments

在我看來(lái),前六個(gè)主要是非常積極的注釋形式,而***三個(gè)有點(diǎn)值得懷疑。在接下來(lái)的部分中,我將使用Redis源代碼中的示例分析每種注釋類(lèi)型。

函數(shù)注釋

函數(shù)注釋的目標(biāo)是防止讀者直接閱讀代碼。

在閱讀注釋之后,讀者應(yīng)該可以將一些代碼視為應(yīng)遵守某些規(guī)則的黑箱子。通常情況下,函數(shù)注釋位于函數(shù)定義的頂部。

rax.c:

 

  1. / * 在當(dāng)前節(jié)點(diǎn)的子樹(shù)中尋找***的key。 
  2.     如果內(nèi)存不足返回0,否則 返回1. * / 
  3. int raxSeekGreatest(raxIterator * it){ 
  4.     ... 

函數(shù)注釋實(shí)際上是一種內(nèi)聯(lián)API文檔。如果函數(shù)注釋編寫(xiě)得好,那么用戶(hù)在大多數(shù)時(shí)候能跳回到她正在閱讀的內(nèi)容(如閱讀調(diào)用此類(lèi)API的代碼),而無(wú)需閱讀函數(shù)(function),類(lèi)(class),宏(macro)等的實(shí)現(xiàn)過(guò)程。

在所有注釋類(lèi)型中,函數(shù)注釋被整個(gè)編程界廣泛接受和需要。要分析的唯一一點(diǎn)是:在代碼內(nèi)部放置以API參考文檔為主的注釋是否是件好事。

對(duì)我來(lái)說(shuō)答案很簡(jiǎn)單:我希望API文檔與代碼完全匹配。隨著代碼的更改,文檔也得到更改。出于這個(gè)原因,我們將函數(shù)注釋用作函數(shù)或其他元素的序言,使API文檔接近代碼,完成三個(gè)任務(wù):

* 隨著代碼的更改,我們可以輕松更改文檔,API參考也不會(huì)有過(guò)時(shí)的風(fēng)險(xiǎn)。

* 這種方法使得更改者(理應(yīng)是最清楚更改目的的人)在***限度上成為API文檔的更改者。

* 讀者能通過(guò)閱讀代碼直接找到函數(shù)或方法(method)的文檔,以便閱讀代碼的讀者只關(guān)注代碼,而不是代碼和文檔之間的上下文切換。

設(shè)計(jì)注釋

“函數(shù)注釋”通常位于函數(shù)的開(kāi)頭,而設(shè)計(jì)注釋通常位于文件的開(kāi)頭。

設(shè)計(jì)注釋一般說(shuō)明了給定代碼片段使用某些算法、技術(shù)、技巧和具體實(shí)現(xiàn)的方式和原因,對(duì)代碼中實(shí)現(xiàn)的內(nèi)容進(jìn)行了更高級(jí)別的概述。在這樣的背景下,閱讀代碼會(huì)更簡(jiǎn)單一些。

bio.c

 

  1. *設(shè)計(jì) 
  2. ------ 
  3. *設(shè)計(jì)很簡(jiǎn)單,我們用一個(gè)結(jié)構(gòu)代表要執(zhí)行的一項(xiàng) Job 
  4. *每種Job類(lèi)型有不同的線程和Job隊(duì)列。 
  5. *每個(gè)線程都在等待隊(duì)列中的新Job,并按照順序處理 
  6. *每個(gè)Job。 
  7.  ... 

原因注釋

原因注釋解釋了代碼執(zhí)行某些操作的原因——即使代碼執(zhí)行的操作非常明確。請(qǐng)看以下來(lái)自Redis replication的代碼 的示例。

replication.c:

 

  1. if(idle> server.repl_backlog_time_limit){ 
  2.  
  3. /* 當(dāng)我們釋放 backlog時(shí),我們總是使用新的 
  4.  * replication ID并清除ID2。這是 
  5.  * 因?yàn)樵跊](méi)有backlog時(shí),master_repl_offset 
  6.  * 未更新,但我們?nèi)詴?huì)保留我們的 
  7.  * replication ID,由此導(dǎo)致以下問(wèn)題: 
  8.  * 
  9.  * 1.我們是一個(gè)主實(shí)例(master instance)。 
  10.  * 2.我們的副本成為主服務(wù)器(Master)。repl-id-2將會(huì) 
  11.  *   與我們的repl-id相同。 
  12.  * 3.我們作為主服務(wù)器,收到了一些更新命令,但不會(huì) 
  13.  *   增加master_repl_offset。 
  14.  * 4.稍后我們將變成副本,連接到新的 
  15.  *   主服務(wù)器,它將接受我們第二個(gè)副本ID的 
  16.  *   PSYNC請(qǐng)求,但會(huì)有數(shù)據(jù)不一致的情況 
  17.  *   因?yàn)槲覀兘邮芰藢?xiě)命令。* / 
  18.  
  19. changeReplicationId(); 
  20. clearReplicationId2(); 
  21. freeReplicationBacklog(); 
  22. serverLog(LL_NOTICE, 
  23.     "Replication backlog freed after %d seconds " 
  24.     "without connected replicas."
  25.     (int) server.repl_backlog_time_limit); 

如果我只檢查函數(shù)調(diào)用,就沒(méi)什么需要糾結(jié)的:如果超時(shí)了就更改主replication ID,清除輔助ID,***釋放replication backlog。

教學(xué)注釋

教學(xué)注釋不會(huì)試圖解釋代碼本身或我們應(yīng)該注意的某些副作用。教學(xué)注釋教授的是代碼運(yùn)行的“領(lǐng)域”(例如數(shù)學(xué),計(jì)算機(jī)圖形學(xué),網(wǎng)絡(luò)系統(tǒng),統(tǒng)計(jì),復(fù)雜的數(shù)據(jù)結(jié)構(gòu)等),這些信息可能超出了讀者的認(rèn)知范圍,或者細(xì)節(jié)多到難以回憶。

版本5中的LOLWUT命令需要在屏幕上顯示旋轉(zhuǎn)的方塊。為了做到這一點(diǎn),它使用了一些基本的三角函數(shù):盡管涉及的數(shù)學(xué)內(nèi)容很簡(jiǎn)單,但許多閱讀Redis源代碼的程序員可能沒(méi)有任何數(shù)學(xué)背景知識(shí),因此函數(shù)頂部的注釋解釋了該函數(shù)的原理。

 

  1. /* 
  2. * 繪制一個(gè)以指定的x,y坐標(biāo)為中心的正方形 
  3. * 旋轉(zhuǎn)角度和大小已定。為了寫(xiě)出旋轉(zhuǎn)方塊的代碼,我們使用了 
  4. * 參數(shù)方程: 
  5. * x = sin(k) 
  6. * y = cos(k) 
  7. * 繪制一個(gè)圓(0-2*PI)。然后,如果我們從45度 
  8. * 開(kāi)始,即k = PI / 4,以此作為***個(gè)點(diǎn),然后我們發(fā)現(xiàn) 
  9. * 其他三個(gè)點(diǎn)的K值以PI / 2(90度)遞增,于是我們得到 
  10. * 了構(gòu)成一個(gè)圓的點(diǎn)。為了旋轉(zhuǎn)方塊,我們從 
  11. * k = PI / 4 + rotation_angle開(kāi)始,然后我們就完事兒了。 
  12. * ...... 
  13. * / 

注釋不包含任何與函數(shù)本身的代碼,或其副作用,或與函數(shù)相關(guān)的技術(shù)細(xì)節(jié)等內(nèi)容。注釋描述的部分僅限于函數(shù)內(nèi)部使用以達(dá)到給定目標(biāo)的數(shù)學(xué)概念。

清單注釋

這是一個(gè)非常常見(jiàn)且奇怪的問(wèn)題:有時(shí)由于語(yǔ)言限制,設(shè)計(jì)問(wèn)題,或者僅僅因?yàn)橄到y(tǒng)內(nèi)部固有的復(fù)雜性,我們無(wú)法將某個(gè)概念或界面集中在一個(gè)代碼片段中,因此代碼中有一些部分能提醒你在代碼的某個(gè)部分做某件事。一般概念是:

 

  1. / * 警告:如果你在此處添加類(lèi)型ID,請(qǐng)務(wù)必修改 
  2.  
  3.    * getTypeNameByID()函數(shù)。* / 

 

在一個(gè)***世界中,我們永遠(yuǎn)不需要添加這類(lèi)注釋;但在實(shí)踐中有時(shí)沒(méi)法省略這一步。

在這種情況下,防御性注釋有時(shí)能起作用:如果你修改了某節(jié)代碼,它會(huì)提醒你修改代碼的其他相關(guān)部分。具體而言,清單注釋會(huì)發(fā)揮以下一種作用(或者兩種兼而有之):

* 告訴你在修改某些內(nèi)容時(shí)要執(zhí)行的一系列操作。

* 警告你應(yīng)該如何進(jìn)行某些更改。

引導(dǎo)注釋

我濫用引導(dǎo)注釋到這種程度:Redis中的大多數(shù)注釋都是引導(dǎo)注釋。然而,引導(dǎo)注釋正是大多數(shù)人認(rèn)知中那類(lèi)完全無(wú)用的注釋?zhuān)?/p>

* 他們沒(méi)有說(shuō)明代碼中不甚明了的內(nèi)容。

* 指導(dǎo)注釋不提供有關(guān)設(shè)計(jì)方面的提示。

引導(dǎo)注釋只做了一件事:他們照顧了讀者的需求,在讀者處理源代碼中的內(nèi)容時(shí)提供明確的劃分(division)和節(jié)奏(rhythm),并介紹接下來(lái)需要閱讀的內(nèi)容。

rax.c

 

  1. / *調(diào)用節(jié)點(diǎn)回調(diào)(如果有的話),如果回調(diào)返回true 
  2.   *則替換節(jié)點(diǎn)指標(biāo)* / 
  3. if (it->node_cb && it->node_cb(&it->node)) 
  4.     memcpy(cp,&it->node,sizeof(it->node)); 
  5.  
  6. /*對(duì)于“下一步”,每次找到一個(gè)鍵就停止 
  7. *一次,因?yàn)橄啾容^后面子節(jié)點(diǎn)分支中的內(nèi)容 
  8. *鍵本身字典序較小。* / 
  9. if (it->node->iskey) { 
  10.     it->data = raxGetData(it->node); 
  11.     return 1; 

Redis內(nèi)“實(shí)際上”充滿(mǎn)了引導(dǎo)注釋?zhuān)曰旧夏愦蜷_(kāi)的每個(gè)文件都會(huì)包含很多引導(dǎo)注釋。為什么要費(fèi)這個(gè)力氣呢?在這篇博客文章中所分析的所有注釋類(lèi)型中,這絕對(duì)是最主觀的一種。我并不覺(jué)得沒(méi)有引導(dǎo)注釋的代碼就不是好代碼。但我堅(jiān)信,如果人們認(rèn)為Redis代碼是可讀的,部分原因就在于其中的引導(dǎo)注釋。

引導(dǎo)注釋還有一些別的用處。因?yàn)樗鼈兠鞔_地將代碼劃分為獨(dú)立的部分,所以我們能在合適的位置插入新代碼,而不是隨便加在其他代碼后面。在代碼附近設(shè)置相關(guān)語(yǔ)句能大大提高可讀性。

引導(dǎo)注釋能簡(jiǎn)要地告訴讀者函數(shù)將要執(zhí)行什么操作,所以如果你只對(duì)大框架感興趣,則無(wú)需回過(guò)頭去閱讀函數(shù)。

瑣碎注釋

引導(dǎo)注釋是非常主觀的工具。不管你喜不喜歡,我反正超愛(ài)引導(dǎo)注釋。

然而,引導(dǎo)注釋可能會(huì)退化為極其糟糕的注釋?zhuān)核苋菀鬃兂?ldquo;瑣碎注釋”(trivial comment)。

瑣碎注釋這種引導(dǎo)注釋所帶來(lái)的認(rèn)知負(fù)荷和僅閱讀相關(guān)代碼比起來(lái)相差無(wú)幾,甚至可能更高。以下這種瑣碎注釋正是許多書(shū)籍規(guī)勸你避免的。

array_len ++; / *增加數(shù)組的長(zhǎng)度。* /

因此,如果你寫(xiě)引導(dǎo)注釋的話,請(qǐng)避免寫(xiě)瑣碎注釋。

(代碼)負(fù)債注釋

負(fù)債注釋是源代碼內(nèi)部硬編碼的技術(shù)債務(wù)語(yǔ)句:

 

  1. entries -= to_delete; 
  2. marked_deleted += to_delete; 
  3. if (entries + marked_deleted > 10 && marked_deleted > entries/2) {     
  4.   / * TODO:執(zhí)行垃圾收集操作。* / 

FIXME,TODO,XXX,“這是一個(gè)黑客”,這些都是負(fù)債注釋。總的來(lái)說(shuō)這些注釋不算好,我試圖避免使用它們,但看起來(lái)不太可能。有時(shí)候,比起永遠(yuǎn)忘記一個(gè)問(wèn)題,我更喜歡在源代碼中放置一個(gè)節(jié)點(diǎn)。程序員至少應(yīng)該定期查看這些注釋?zhuān)纯词欠窨梢阅芨倪M(jìn)一下表述,或者這些問(wèn)題是否已不再相關(guān)或可以立即解決。

備份注釋

備份注釋是開(kāi)發(fā)人員對(duì)某些代碼塊的舊版本甚至是整個(gè)函數(shù)做出的注釋?zhuān)驗(yàn)樗?她對(duì)新版本中運(yùn)行的更改放不下心。令人費(fèi)解的是,現(xiàn)在有了Git,人們卻還在使用這類(lèi)注釋。我想人們對(duì)于丟失代碼片段有一種不安全感,過(guò)去提交代碼時(shí),使用備份注釋會(huì)顯得更加理智可靠。

但源代碼并不是用來(lái)備份的。如果你需要保存舊版本的函數(shù)或代碼,說(shuō)明你的工作尚未完成,也無(wú)法提交。要么確保新函數(shù)比過(guò)去的更好,要么只在開(kāi)發(fā)樹(shù)(development tree)中使用它,直到你確定為止。

備份注釋是我分類(lèi)中的***一項(xiàng)。我們來(lái)做個(gè)總結(jié)。

總結(jié)

注釋是和未來(lái)的代碼讀者聊天,讀者們還能在Twitter上評(píng)價(jià)你的注釋。所以在這個(gè)過(guò)程中,你真心地在審視自己所注釋的內(nèi)容是否“能讓人接受”,看自己寫(xiě)得是否足夠體面、足夠好。如果不是,你就勤勤懇懇地再做一遍,拿出更好的注釋來(lái)。

你可能認(rèn)為編寫(xiě)注釋不是個(gè)高端工作。畢竟你“會(huì)寫(xiě)代碼”!但請(qǐng)考慮這一點(diǎn):代碼是一組語(yǔ)句和函數(shù)調(diào)用(或者你做的其他編程范例也一樣)。如果代碼寫(xiě)得不好,這些語(yǔ)句就沒(méi)有多大意義。注釋常常要求你進(jìn)行一些設(shè)計(jì)過(guò)程,并從更深層次來(lái)理解你正在編寫(xiě)的代碼。

最重要的是,為了寫(xiě)出好的注釋?zhuān)惚仨毰囵B(yǎng)自己的寫(xiě)作能力。這種寫(xiě)作技巧能幫你更好地編寫(xiě)電子郵件、文案、設(shè)計(jì)文檔、博客文章和提交文件。

我寫(xiě)代碼是因?yàn)槲移惹邢胍c他人溝通交流、分享想法。注釋能夠?yàn)榇a提供幫助,把作者的心血表現(xiàn)出來(lái)。說(shuō)到底,我喜歡寫(xiě)注釋?zhuān)拖裎蚁矚g寫(xiě)代碼一樣。

注:英文原文太長(zhǎng),翻譯后有刪減。 

責(zé)任編輯:龐桂玉 來(lái)源: 快資訊
相關(guān)推薦

2013-05-02 09:36:44

代碼項(xiàng)目

2015-05-21 10:43:36

工程師如何寫(xiě)代碼

2020-12-14 08:17:50

代碼

2024-10-29 09:25:00

2011-05-26 09:39:53

程序

2010-01-04 10:07:03

程序員

2014-11-26 14:46:47

代碼

2020-07-15 08:17:16

代碼

2020-05-11 15:23:58

CQRS代碼命令

2021-09-01 08:55:20

JavaScript代碼開(kāi)發(fā)

2013-06-07 14:00:23

代碼維護(hù)

2021-11-30 10:20:24

JavaScript代碼前端

2022-12-06 09:03:44

代碼fork系統(tǒng)

2021-01-04 07:57:07

C++工具代碼

2022-02-08 19:33:13

技巧代碼格式

2022-02-17 10:05:21

CSS代碼前端

2021-10-13 08:16:14

SQL 代碼系統(tǒng)

2020-12-19 10:45:08

Python代碼開(kāi)發(fā)

2020-05-19 15:00:26

Bug代碼語(yǔ)言

2019-09-20 15:47:24

代碼JavaScript副作用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)