0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

函數(shù)的設(shè)計(jì)功能是什么

馬哥Linux運(yùn)維 ? 來(lái)源:馬哥Linux運(yùn)維 ? 作者:馬哥Linux運(yùn)維 ? 2022-11-01 10:19 ? 次閱讀

每隔一段時(shí)間,網(wǎng)上總會(huì)突然出現(xiàn)一些令人討厭的帖子,其觀點(diǎn)是:不應(yīng)該為代碼寫注釋,它存在的唯一原因是因?yàn)榇a本身不足夠好。對(duì)于這些論點(diǎn),我完全不能茍同。

爛代碼

他們的觀點(diǎn)也不完全是錯(cuò)誤的。沒(méi)有人能說(shuō)自己的代碼足夠好。代碼本身也會(huì)慢慢變壞。你知道什么時(shí)候代碼腐爛得最厲害嗎?當(dāng)你六個(gè)月沒(méi)有碰這些代碼的時(shí)候!

當(dāng)回過(guò)頭再讀的時(shí)候,你會(huì)非常好奇:“這個(gè)作者到底是怎么想的?”(于是,使用 Git blame 來(lái)查看歷史記錄,沒(méi)想到代碼竟然是自己寫的,因?yàn)檫@是你的代碼。)

反對(duì)注釋者的論點(diǎn)是:需要注釋的唯一原因是你的代碼不夠“清晰”。如果代碼重構(gòu)、命名和組織地更好,那就不需要這些注釋了。

今天,當(dāng)整個(gè)項(xiàng)目和問(wèn)題空間都裝在你的腦袋里的時(shí)候,你自然會(huì)覺(jué)得代碼是干凈、清晰和優(yōu)雅的。但是,當(dāng)六個(gè)月后,又或是 CTO 剛好在生產(chǎn)系統(tǒng)上突然發(fā)現(xiàn)一個(gè)非常嚴(yán)重的 bug,在主管緊盯的情況下,某個(gè)可憐的家伙不得不去調(diào)試你的代碼的時(shí)候,這些代碼可能對(duì)你已經(jīng)有些模糊了。

你無(wú)比熟悉的一段代碼,嘗試去理解其他人在什么場(chǎng)景下不能理解,這是一種非常難以掌握的技能。不過(guò),它具有無(wú)可估量的價(jià)值,幾乎和一次就能把代碼寫到位的能力一樣重要。在工業(yè)界中,基本上沒(méi)有人是獨(dú)行俠。即使真地在獨(dú)自寫代碼,你也會(huì)遺忘代碼這么寫的緣由或者昨天深夜“工程代碼”核心部分的確切目的。未來(lái)一旦你離職,接替你的人不得不去理解每一個(gè)僅藏在你的腦袋里的小偏好和習(xí)慣。

所以,寫上一個(gè)即使在現(xiàn)在看來(lái)過(guò)于淺顯的注釋也不是一個(gè)壞事情。有時(shí)候,它甚至?xí)?lái)巨大的幫助。

無(wú)注釋經(jīng)常導(dǎo)致代碼更難以理解

某些人聲稱:移除注釋將會(huì)使代碼變得更好,因?yàn)檫@迫使你編寫更清晰的代碼。我對(duì)此亦不以為然,因?yàn)槲也徽J(rèn)為有人會(huì)實(shí)際寫上一些次佳的代碼,并且寫上一些注釋來(lái)解釋這種行為(除了// TODO: 這是一個(gè)臨時(shí)的解決方法,我會(huì)稍后修正之外)。我們都會(huì)寫出在各種外部條件(通常是時(shí)間)下自認(rèn)為最好的代碼。

為去除注釋而重構(gòu)代碼的問(wèn)題在于,這種努力往往事與愿違,會(huì)產(chǎn)出更壞的代碼。典型例子是重構(gòu)一行復(fù)雜的代碼,將之提取到一個(gè)獨(dú)立的函數(shù)中,并取一個(gè)望文生義的名字。這個(gè)行為看上去很棒,但是,現(xiàn)在你為閱讀代碼的人帶來(lái)了一個(gè)上下文切換點(diǎn)。替代真實(shí)代碼的是一個(gè)函數(shù)調(diào)用,于是,他們不得不滾動(dòng)到函數(shù)定義的地方,記住和對(duì)照函數(shù)聲明和調(diào)用的參數(shù),并且將函數(shù)返回值代入到調(diào)用的地方。

另外,清晰的函數(shù)名僅僅能夠提供非常短小的注釋。任何需要多余一小段短語(yǔ)的注釋無(wú)法(或者不應(yīng)該)概括到一個(gè)函數(shù)名中。因此,你最終會(huì)得到一個(gè)其上有注釋的函數(shù)。

的確,一個(gè)非常短小的函數(shù)都可能導(dǎo)致困惑和更復(fù)雜的代碼。如果看到這樣的函數(shù),我會(huì)去搜索這個(gè)函數(shù)在哪些地方被調(diào)用。如果只有一個(gè)地方,我就會(huì)去思考,這是一個(gè)確實(shí)封裝了全局邏輯的通用代碼塊呢(譬如 NameToUserID),還是,這個(gè)函數(shù)嚴(yán)重依賴調(diào)用端的特定狀態(tài)和實(shí)現(xiàn),并且不能在其他地方正確工作。隨著把這些代碼提取一個(gè)函數(shù)里面,你本質(zhì)上在其余的代碼庫(kù)中暴露了這些實(shí)現(xiàn)細(xì)節(jié),這么草率的做決定是不合適的。即使你知道這個(gè)函數(shù)其他人不應(yīng)該調(diào)用,其他人還會(huì)在某些地方調(diào)用它,即便這些地方不合適這么做。

小函數(shù)的相關(guān)問(wèn)題在 Cindy Sridharan 在 medium 網(wǎng)站上的帖子[1]中有更加詳細(xì)的闡述。

我們甚至可以深入討論長(zhǎng)短變量名的比較和權(quán)衡,但是就此打住吧,一般你不可能接受更長(zhǎng)的變量名了。除非你的變量名就是你想寫的完整的注釋,否則你還是會(huì)丟失信息而不得不添加到注釋中。我認(rèn)為我們可以達(dá)成一致:usernameStrippedOfSpacesWithDotCSVExtension 是一個(gè)可怕的變量名稱。

我不是說(shuō)我們不應(yīng)該提煉代碼,讓它們更加清晰和優(yōu)雅。絕對(duì)要這么做!這是一個(gè)杰出開(kāi)發(fā)人員的特征。但是,代碼清晰性和有注釋是正交的,撰寫良好的注釋也是杰出開(kāi)發(fā)人員的特征。

沒(méi)有壞注釋

在這些討論中給出的壞注釋的例子都是些小錯(cuò)誤,除了那些啟蒙編程課程外,在實(shí)際工作中幾乎不會(huì)碰到。

// 實(shí)例化一個(gè)錯(cuò)誤對(duì)象
var err error

不錯(cuò),這個(gè)注釋很清楚,但不是非常有用。不過(guò)同時(shí),它實(shí)際上也沒(méi)有什么壞處。

在瀏覽代碼時(shí),雖然有些不待見(jiàn),但也很容易被忽略。如果開(kāi)發(fā)者能夠在其中包含一個(gè)有用的注釋,能夠節(jié)省我數(shù)小時(shí)鍵盤工作時(shí)間的話,我寧愿看成百這樣的簡(jiǎn)單注釋,而不是沒(méi)注釋。

我非常確信,不會(huì)有任何代碼會(huì)說(shuō)“伙計(jì),這段代碼非常容易理解,所以不需要提供任何注釋?!?實(shí)際情況恰恰完全相反。

實(shí)際上,我找到了一些嚴(yán)重缺失注釋的代碼 - Go 標(biāo)準(zhǔn)庫(kù)。它的代碼非常精良,但在很多情況下,如果在讀取代碼前對(duì)其功能沒(méi)有深刻理解,那么理解他們?yōu)槭裁催@么設(shè)計(jì)將是個(gè)挑戰(zhàn)。如果能加一些注釋,用于解釋代碼的邏輯和設(shè)計(jì)意圖,將使 Go 標(biāo)準(zhǔn)庫(kù)更加容易閱讀。在這篇文章中,我主要討論實(shí)現(xiàn)代碼里的注釋,而不是通常的公開(kāi)函數(shù)的文檔注釋(通常情況下,它們也是非常棒的)。

任何注釋勝過(guò)無(wú)注釋

另外一個(gè)反注釋者喜歡拿出來(lái)的例子,可以用下面的簡(jiǎn)潔有力的圖片來(lái)展示(證明其論點(diǎn)):

哈,極好笑的,有人更換了瓶子里面的東西但是沒(méi)有更新外面的標(biāo)簽。

但是,這是 20 年前的問(wèn)題了,當(dāng)時(shí)通常不進(jìn)行代碼審查。不過(guò),現(xiàn)在代碼審查已經(jīng)非常普遍了。如果檢查注釋和實(shí)現(xiàn)是否匹配不是你們代碼審核流程的一部分,那么最好檢查一下你們的代碼審核流程。

這不是說(shuō)不會(huì)犯錯(cuò)誤,實(shí)際上我昨天剛提交了一個(gè)“注釋和實(shí)現(xiàn)不一致”的 bug。類似“無(wú)注釋比錯(cuò)誤注釋好”的言論初聽(tīng)起來(lái)是正確的,然而,當(dāng)你認(rèn)識(shí)到如果沒(méi)有注釋,開(kāi)發(fā)人員猜錯(cuò)代碼的功能比錯(cuò)誤注釋的出現(xiàn)的概率高的多的時(shí)候,你會(huì)改變你的看法。

即使這種情況真的發(fā)生,代碼被修改了,你依然可以獲取有價(jià)值的信息:代碼以前的用途。修改僅僅和原先有些許不同罷了,它依舊完成基本相同的功能。為了版本控制和向后兼容,同一個(gè)函數(shù)在不改變名稱和簽名的情況下,在功能上發(fā)生劇烈變化的頻率有多少?基本上非常少。

就拿我昨天發(fā)現(xiàn)的 bug 來(lái)說(shuō),我們調(diào)用 client.SetKeepAlive(60)。而 SetKeepAlive 函數(shù)的注釋是 “SetKeepAlive 在發(fā)送 PING 請(qǐng)求之前,客戶端需要等待指定數(shù)量的時(shí)間(以秒為單位)”??瓷先ズ馨?,不是嗎?知道我注意到 SetKeepAlive 的參數(shù)是 time.Duration。

如果沒(méi)有其他指定的單位,60 這個(gè)整數(shù)將使用 Go 的 duration 的缺省單位納秒。哎,某人更新了該函數(shù),使用 Duration 類型來(lái)替換 Int。有趣的是,它仍然向下取整到了最接近的秒數(shù),所以注釋不是不正確,只是有些誤導(dǎo)罷了。

為什么?

最重要的注釋是為什么要注釋。為什么代碼是按照設(shè)計(jì)來(lái)執(zhí)行的?為什么這個(gè) ID 需要小于 24 個(gè)字符?為什么要在 Linux 下面隱藏這個(gè)選項(xiàng)?諸如此類。這些問(wèn)題為什么重要的原因是你無(wú)法從代碼中提煉出來(lái)。這些注釋總結(jié)了開(kāi)發(fā)者獲得的經(jīng)驗(yàn)教訓(xùn),商業(yè)或系統(tǒng)層面的限制條件等,它們是價(jià)值無(wú)量的,并且?guī)缀鯚o(wú)法從其他途徑獲得(例如,函數(shù)取名應(yīng)該反映函數(shù)做什么而不是為什么)。

那些用于說(shuō)明代碼功能的注釋往往不是特別有用的,因?yàn)槿绻麚碛凶銐虻臅r(shí)間和努力,你總能夠理解代碼的功能。本質(zhì)上,通過(guò)函數(shù)定義,代碼往往會(huì)告訴你它的具體功能,但這不意味著你不應(yīng)該寫任何注釋。確實(shí)應(yīng)該力爭(zhēng)寫出最清晰簡(jiǎn)潔的代碼,但是注釋不需要任何額外的運(yùn)行時(shí)開(kāi)銷,如果你覺(jué)得有人會(huì)錯(cuò)誤理解一些代碼或者理解上有困難,應(yīng)該寫上一些注釋。至少,這個(gè)會(huì)節(jié)省他們半個(gè)小時(shí)來(lái)理解你的代碼,這些注釋也會(huì)在很大程度上幫助他們避免錯(cuò)誤地修改或使用你的代碼,從而導(dǎo)致 bug 的產(chǎn)生。

測(cè)試

一些人認(rèn)為函數(shù)的功能測(cè)試案例就相當(dāng)于文檔。某種程度上說(shuō),確實(shí)是這樣的。但是,在我的效率文檔表中,它的優(yōu)先級(jí)非常低。為什么呢?因?yàn)樗鼈儤O其精確而且瑣碎,僅僅覆蓋了功能的很少一部分。每一個(gè)測(cè)試僅確切地測(cè)試一個(gè)特定的輸入和與之相配的輸出。任何超過(guò)一個(gè)簡(jiǎn)單函數(shù)的情況,你很可能需要一大串代碼來(lái)構(gòu)建輸入和輸出。

對(duì)于大多數(shù)編碼而言,描述一個(gè)函數(shù)的主要功能比寫代碼去完整測(cè)試要容易的多。

很多時(shí)候我的測(cè)試代碼行數(shù)倍于函數(shù)實(shí)現(xiàn)本身,然而文檔注釋僅僅需要寥寥幾行而已。

此外,測(cè)試僅僅解釋了函數(shù)的功能。函數(shù)的設(shè)計(jì)功能是什么?它們不能解釋為什么,但是就像前面提到的,設(shè)計(jì)目的和意圖總是更重要的。

你確實(shí)應(yīng)當(dāng)測(cè)試你的代碼,通過(guò)一些邊界測(cè)試案例,測(cè)試對(duì)于判定代碼在邊界條件下是否能夠正常工作非常有用。但是一般而言,如果到了必須通過(guò)閱讀測(cè)試案例來(lái)理解代碼的地步的話,那么已經(jīng)是一個(gè)危險(xiǎn)信號(hào),告訴我們需要去編寫更多更好的注釋了。

結(jié)論

除了一些非常簡(jiǎn)單的例子以外, 有用注釋和無(wú)用注釋的邊界是非常難于去發(fā)現(xiàn)的。

所以,我寧愿人們站在多寫注釋的一方。你無(wú)法知道下一個(gè)可能閱讀你代碼的人是誰(shuí),所以能幫助他們的是盡你所能寫上一大堆的注釋。盡量寫到你認(rèn)為太多了,然后再多寫一些,這個(gè)數(shù)量估計(jì)就正好了。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4238

    瀏覽量

    61976
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4672

    瀏覽量

    67779
  • BUG
    BUG
    +關(guān)注

    關(guān)注

    0

    文章

    155

    瀏覽量

    15628

原文標(biāo)題:淺談 Go 語(yǔ)言代碼注釋問(wèn)題

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    tcpip_adapter_start_api 函數(shù)功能是什么?

    , msg->mac, msg->ip_info); } 請(qǐng)問(wèn) 1. tcpip_adapter_start_api函數(shù)功能是什么??或者說(shuō)tcpip_adapter_start中調(diào)用
    發(fā)表于 06-26 07:08

    接口測(cè)試的工具有哪些種類

    單元測(cè)試框架 單元測(cè)試框架主要用于測(cè)試單個(gè)模塊或函數(shù)功能。雖然它們主要用于開(kāi)發(fā)階段,但也可以用于接口測(cè)試。 1.1 JUnit (Java) JUnit 是 Java 語(yǔ)言的單元測(cè)試框架,支持自動(dòng)化測(cè)試,可以測(cè)試 Java 編寫的接口。 1.2 NUnit (C#)
    的頭像 發(fā)表于 05-30 15:07 ?427次閱讀

    求助,關(guān)于在STM8S用FOR遇到的疑問(wèn)求解

    我用了一塊STM8F103F3P,TSSOP20封裝的最小系統(tǒng)板,直接連接WS2811的燈條,用數(shù)組來(lái)存貯要發(fā)送的數(shù)據(jù),發(fā)送是這樣寫的: /*********************** 函數(shù)功能
    發(fā)表于 05-16 07:30

    STM32F3執(zhí)行函數(shù)的時(shí)候進(jìn)入HardFault_Handler死循環(huán),怎么處理?

    + (uint32_t)(dataRx1 << 16)); return *Result; } 執(zhí)行這個(gè)函數(shù)的時(shí)候進(jìn)入HardFault_Handler死循環(huán),求助怎么處理?函數(shù)功能是從某個(gè)芯片中讀取兩個(gè)字,網(wǎng)上
    發(fā)表于 05-13 07:35

    STM32片上flash能否讀取正在運(yùn)行的代碼段內(nèi)容?

    如題 小弟想請(qǐng)教下大家 STM32片上flash的讀取問(wèn)題。舉個(gè)例子: 函數(shù)功能是 讀取片上flash 0x0800_1000~0x0800_2000的存儲(chǔ)內(nèi)容 函數(shù)存儲(chǔ)在flash的 0x0800_1000~0x0800_2
    發(fā)表于 04-16 07:22

    現(xiàn)場(chǎng)可編程門陣列的基本結(jié)構(gòu)和優(yōu)缺點(diǎn)

    主要由查找表(LUT)和D觸發(fā)器(DFF)等邏輯電路構(gòu)成。查找表用于實(shí)現(xiàn)邏輯函數(shù)功能,而D觸發(fā)器則用于存儲(chǔ)邏輯狀態(tài)。嵌入式塊RAM提供了額外的存儲(chǔ)空間,布線資源則負(fù)責(zé)各個(gè)邏輯單元之間的連接。
    的頭像 發(fā)表于 03-27 14:49 ?422次閱讀

    使用CubeMX配置STM32010C6T6的LPUART1外設(shè),調(diào)用串口發(fā)送和接收函數(shù)均不能收發(fā)數(shù)據(jù)怎么解決?

    串口初始化文件中將RX引腳模式更改為輸入類型同樣不能接收到數(shù)據(jù) 在CubeMX中更換MCU型號(hào)為F103系列并配置串口,則RX引腳默認(rèn)為輸入模式,并且生成的工程調(diào)用串口收發(fā)函數(shù)功能正常 這種現(xiàn)象是不是CubeMX軟件導(dǎo)致的問(wèn)題呢,應(yīng)該如何解決(生成的工程默認(rèn)沒(méi)有使能串口,
    發(fā)表于 03-19 07:48

    ADT7310讀取數(shù)據(jù)波動(dòng)較大是什么原因?qū)е碌模?/a>

    PORTB= ~BIT(CS) //片選關(guān) /* 函數(shù)功能: SPI初始化 */ void Spi_Init(void) { //使能SPI,先發(fā)送MSB //主機(jī)模式,SPI工作在模式0
    發(fā)表于 01-02 07:37

    ADXL355為什么不能進(jìn)入測(cè)量模式?

    抓取到的數(shù)據(jù)波形,以及代碼: 這里是抓取到的讀取ID寄存器的數(shù)據(jù)波形 這里是寫入電源控制寄存器的值0x00 讀取到的電源控制寄存器與溫度寄存器的值 以下是代碼 /* 函數(shù)功能:初始化
    發(fā)表于 12-28 06:40

    如何規(guī)范嵌入式C編碼注釋以及排版與格式

    注釋 ◎ 注釋應(yīng)放在其代碼上方相鄰位置或右方,不可放在下面。 ◎ 注釋的內(nèi)容要清楚明了,防止注釋二義性。 ◎ 修改代碼時(shí)同步更新注釋,保證注釋與代碼的一致性。 ◎ 函數(shù)聲明處注釋描述函數(shù)功能、性能
    的頭像 發(fā)表于 12-07 14:53 ?481次閱讀

    wps有vlookup的功能

    WPS是一款常用的辦公軟件套件,其中包括WPS文字、WPS表格和WPS演示等多個(gè)功能模塊。在WPS表格中,確實(shí)有類似于Excel的VLOOKUP函數(shù)功能,稱為VLOOKUP函數(shù)。 V
    的頭像 發(fā)表于 12-01 11:05 ?1074次閱讀

    IF和ISNA和Vlookup函數(shù)一起使用

    這三個(gè)函數(shù)功能和使用方法,并通過(guò)實(shí)例演示它們的應(yīng)用。 首先,我們來(lái)介紹IF函數(shù)。IF函數(shù)是一種邏輯函數(shù),它根據(jù)一個(gè)條件的滿足與否,返回不同
    的頭像 發(fā)表于 11-30 16:25 ?3698次閱讀

    c語(yǔ)言源程序的基本單位

    個(gè)或多個(gè)函數(shù)組成。每個(gè)函數(shù)都有一個(gè)函數(shù)名和一對(duì)大括號(hào){},大括號(hào)中是函數(shù)的代碼塊。函數(shù)可以接受一些參數(shù),并且可以返回一個(gè)值。
    的頭像 發(fā)表于 11-24 10:20 ?1257次閱讀

    python調(diào)用math函數(shù)的方法

    中。本文將詳細(xì)介紹math模塊中的各種數(shù)學(xué)函數(shù)的調(diào)用方法,包括函數(shù)功能、參數(shù)的使用和返回值的含義等方面,以幫助讀者更好地理解和應(yīng)用這些函數(shù)。 一、導(dǎo)入math模塊 要使用math模塊
    的頭像 發(fā)表于 11-22 11:01 ?2112次閱讀

    單片機(jī)編程實(shí)例80例

    【建議收藏】單片機(jī)編程實(shí)例80例大全 *實(shí)例1: 使用P3口流水點(diǎn)亮8位LED #include<reg51.h> //包含單片機(jī)寄存器的頭文件 / 函數(shù)功能:延時(shí)
    發(fā)表于 11-21 12:00