《代碼整潔之道》讀書筆記(三)之寫好注釋很重要

別給糟糕的代碼添加注釋——重新寫吧陨溅。

若編程語言足夠有表達(dá)力终惑,或者我們長于用這些語言來表達(dá)意圖,就不那么需要注釋——也許根本不需要门扇。

注釋的恰當(dāng)用法是彌補(bǔ)我們在用代碼表達(dá)意圖時(shí)遭遇的失敗雹有。如果你發(fā)現(xiàn)自己需要寫注釋,再想想看是否有辦法通過代碼來表達(dá)臼寄。

本文不提倡寫太多注釋霸奕,能用代碼表達(dá)就不要寫注釋。

1. 注釋不能美化糟糕的代碼

寫注釋的常見動(dòng)機(jī)之一是糟糕的代碼的存在吉拳。

帶有少量注釋的整潔而有表達(dá)力的代碼质帅,比帶有大量注釋的零碎而復(fù)雜的代碼像樣的多。與其花時(shí)間編寫解釋你搞出的糟糕的代碼的注釋,不如花時(shí)間清潔那堆糟糕的代碼临梗。

2. 用代碼來闡述

有時(shí)候涡扼,代碼本身不足以解釋其行為。幸運(yùn)的是盟庞,很多時(shí)候我們都可以把代碼寫地能解釋自己的行為吃沪。


// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_DAY) && 
    (employee.age > 65))

改成這樣怎么樣?

if (employee.isEligibleForFullBenefits())

3. 好注釋

有些注釋是必須的什猖,也是有利的票彪。不過要記住,唯一真正的好注釋是你想辦法不去寫的注釋不狮。

3.1 法律信息

版本及著作權(quán)聲明就是有理由在每個(gè)源文件開頭注釋處放置的內(nèi)容

3.2 提供信息的注釋

// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

注釋說明降铸,該正則表達(dá)式意在匹配一個(gè)經(jīng)由SimpleDateFormat.format函數(shù)利用特定格式字符串格式化的時(shí)間和日期。

這類注釋有時(shí)管用摇零,但更好的方式是盡量利用函數(shù)名稱表達(dá)信息推掸。

3.3 對意圖的解釋

有時(shí),注釋不僅提供了有關(guān)實(shí)現(xiàn)的有用信息驻仅,而且還提供了某個(gè)決定后面的意圖谅畅。

下面的例子,你也許不同意程序員給這個(gè)問題提供的解決辦法噪服,但至少你知道他想干什么毡泻。

public void testConcurrentAddWidgets() throws Exception {
    WidgetBuilder widgetBuilder = 
        new WidgetBuilder(new Class[] {BoldWidget.class});
    String text = "'''bold text'";
    ParentWidget parent = 
        new BoldWidget(new MockWidgetRoot(), text);
    
    AtomicBoolean failFlag = new AtomicBoolean();
    failFlag.set(false);
    
    // This is our best attempt to get a race condition 
    // by creating large number of threads
    for (itn  i = 0; i < 25000; i++) {
        WidgetBuilderThread widgetBuilderThread = 
            new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
        new Thread(widgetBuilderThread).start();
    }

    assertEquals(false, failFlag.get());
}

3.4 闡釋

有時(shí)候參數(shù)或者返回值晦澀難懂的時(shí)候,用注釋來幫助闡釋其含義就會(huì)有用粘优。但前提是仇味,你考慮一下是否有更好的辦法,在小心的加上注釋雹顺。

3.5 警示

有時(shí)丹墨,用于警示其他程序員會(huì)出現(xiàn)某種后果的注釋也是有用的。

public static SimpleDateFormat makeStandardHttpDateFormat() {
    // SimpleDateFormat is not thread safe
    // so we need to create each instance independently.
    SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
    df.setTimeZone(TimeZone.getTimeZone("GMT"));
    return df;
}

上面例子也許有更好的解決辦法无拗,不過這個(gè)注釋卻是非常有必要的带到。它能阻止某個(gè)急切的程序員以效率之名使用靜態(tài)初始器。

3.6 TODO注釋

TODO是一種程序員認(rèn)為應(yīng)該做英染,但由于某些原因目前還沒做的工作揽惹。它可能是要提醒刪除某個(gè)不必要的特性,或者要求他人注意某個(gè)問題四康,它可能是懇請別人取個(gè)好名字等等搪搏。

如今大多數(shù)IDE都提供了定位TODO注釋,定期查看這些注釋闪金,刪除不再需要的疯溺,讓代碼整潔论颅。

3.7 放大

注釋可以用來放大某種看起來不合理之物的重要性。

String listItemContent = match.group(3).trim();
// the trim is real important. It removes the starting
// spaces that could cause the item to be recognized 
// as another list
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));

3.8 公共API中的Javadoc

如果你在編寫公共API囱嫩,就該為它編寫良好的Javadoc恃疯。

4. 壞注釋

大多數(shù)注釋都屬此類。通常墨闲,壞注釋都是糟糕代碼的支撐或借口今妄,或者對錯(cuò)誤決策的修正,基本上等于程序員自說自話鸳碧。

4.1 喃喃自語

寫一些只有作者自己知道含義的注釋盾鳞,就是喃喃自語。

4.2 多余的注釋

相比代碼本身無法提供更過的信息就是多余的注釋瞻离。比如沒有證明代碼的意義腾仅,也沒有給出代碼的意圖或邏輯。讀這種注釋還不如讀代碼套利。

4.3 誤導(dǎo)性注釋

注釋不夠精確推励,和代碼本身表達(dá)的含義不一致,容易誤導(dǎo)程序員肉迫。

4.4 循規(guī)式注釋

所謂每個(gè)函數(shù)都要有Javadoc或每個(gè)變量都要有注釋的規(guī)矩全然是愚蠢可笑的吹艇。這類注釋突然讓代碼變得散亂,滿口胡言昂拂,令人迷惑不解。


/**
 * @param title The title of the CD
 * @param author The author of the CD
 * @param tracks The number of tracks on the CD
 * @param durationInMinutes The duration of the CD in minutes
 */
public void addCD(String title, String author, int tracks, int durationInMinutes) {
    CD cd = new CD();
    cd.title = title;
    cd.author = author;
    cd.tracks = tracks;
    cd.duration = durationInMinutes;
    cdList.add(cd);
}

4.5 日志式注釋

有人會(huì)在每次編輯代碼時(shí)抛猖,在模塊開始處添加一條注釋格侯。這類注釋就像是一種記錄每次修改的日志。

很久以前财著,在模塊開始處創(chuàng)建并維護(hù)這些記錄還算有道理联四。那時(shí),我們還沒有源代碼控制系統(tǒng)可用撑教。如今朝墩,這種冗長的記錄只會(huì)讓模塊變得凌亂不堪,應(yīng)當(dāng)全部刪除伟姐。

4.6 廢話注釋

對顯然之事喋喋不休收苏,毫無新意。

/** 
 * Default constructor
 */
protected AnnualDateRule() {
    
}

/** The day of the month. */
private int dayOfMonth;

/**
 * Returns the day of the month
 * @return the day of the month
 */
public int getDayOfMonth() {
    return dayOfMonth;
}

4.7 可怕的廢話

Javadoc也可能是廢話愤兵。下列Javadoc(摘自某知名開源庫)的目的是什么鹿霸? 答案:無。

/** The name. */
private String name;

/** The version */
private String version;

/** The licenseName */
private String licenseName;

/** The version. */
private String info;

再仔細(xì)讀讀這些注釋秆乳。你是否發(fā)現(xiàn)了剪切-粘貼錯(cuò)誤懦鼠? 如果作者在寫(粘貼)注釋的時(shí)候都沒花心思钻哩,怎么能指望讀者從中受益呢?

4.8 能用函數(shù)或變量時(shí)就別用注釋

看看以下代碼概要:

// does the module from the global list <mod> depend on the 
// subsystem we are part of ?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))

可以改成以下沒有注釋的版本:

ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

4.9 位置標(biāo)記

有時(shí)肛冶,程序員喜歡在源代碼中標(biāo)記某個(gè)特別位置街氢。例如:

// Actions //////////////////////////////////////////

把特定函數(shù)放在這種標(biāo)記下面,多數(shù)時(shí)候?qū)崒贌o理睦袖。雞零狗碎珊肃,理當(dāng)刪除——特別是尾部那一長串無用的斜杠。

這么說吧扣泊,如果標(biāo)記欄不多近范,就會(huì)顯而易見。盡量少用這中標(biāo)記欄延蟹。如果濫用评矩,代碼就會(huì)沉沒在背景噪音中,被忽略掉阱飘。

4.10 括號后的注釋

有時(shí)斥杜,程序員會(huì)在括號后面放置特殊的注釋,盡管這對于含有深度嵌套結(jié)構(gòu)的長函數(shù)可能有意義沥匈,但只會(huì)給我們更愿意編寫的短小蔗喂、封裝的函數(shù)帶來混亂。如果你發(fā)現(xiàn)自己想標(biāo)記右括號高帖,其實(shí)應(yīng)該做的是縮短函數(shù)缰儿。

4.11 歸屬于署名

/* Added by Rick */

版本控制系統(tǒng)非常善于記住是誰在何時(shí)添加了什么。沒必要用小小的簽名搞臟代碼散址。

4.12 注釋掉的代碼

直接把代碼注釋掉是討厭的做法乖阵。別這么干!

InputStreamResponse response = new InputStreamResponse();
resonpse.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultsStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultStream);
// response.setContent(reader.read(formatter.getByteCount()));

其他人不敢刪除注釋掉的代碼预麸。他們會(huì)想瞪浸,代碼依然放在那兒,一定有其原因吏祸,而且這段代碼很重要对蒲,不能刪除。注釋掉的代碼堆積在一起贡翘,就像破舊瓶底的渣滓一般蹈矮。

曾經(jīng)有一段時(shí)間,注釋掉代碼可能有用床估。但我們已經(jīng)擁有優(yōu)秀的源代碼控制系統(tǒng)含滴,這些系統(tǒng)可以為我們記住不要的代碼。我們無需再用注釋來標(biāo)記丐巫,刪掉即可谈况,它們丟不了勺美。我擔(dān)保。

4.13 HTML注釋

源碼中包含HTML注釋讓人討厭碑韵,難以閱讀赡茸。

4.14 非本地信息

假如你一定要寫注釋,請確保它描述了離它最近的代碼祝闻。別在本地注釋的上下文環(huán)境中包含出系統(tǒng)級的信息占卧。
下面的例子,除了可怕的冗余之外联喘,還包含了系統(tǒng)級的默認(rèn)端口信息华蜒。但是這個(gè)函數(shù)完全沒控制那個(gè)所謂的默認(rèn)值。假如那個(gè)值更改了豁遭,無法擔(dān)保這個(gè)注釋也會(huì)跟著修改叭喜。

/**
 * port on which fitnesse would run. Default to <b>8082</b>
 * @param fitnessePort
 */
public void setFitnessePort(int fitnessPort) {
    this.fitnessePort = fitnessePort;
}

4.15 信息過多

別在注釋中添加有趣的歷史性話題或者無關(guān)的細(xì)節(jié)描述。

4.16 不明顯的聯(lián)系

注釋及其描述的代碼之間的聯(lián)系應(yīng)該是顯而易見蓖谢。如果你不嫌麻煩要寫注釋捂蕴,至少讓讀者能看著注釋和代碼,并且理解注釋所談何物闪幽。

以來自Apache公共庫的這段注釋為例

/**
 * start with an array that is big enough to hold all the pixels
 * (plus filter bytes), and an extra 200 bytes for header info
 */
this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];

過濾字節(jié)是什么鬼啥辨? 與那個(gè)+1有關(guān)系嗎?或與*3有關(guān)系盯腌? 還是與二者皆有關(guān)系溉知? 注釋的作用是解釋未能自行解釋的代碼。如果注釋本身還需要解釋腕够,就太遺憾了着倾。

4.17 函數(shù)頭

短函數(shù)不需要太多描述。為只做一件事的短函數(shù)取個(gè)好名字燕少,通常要比寫函數(shù)頭注釋要好。

4.18 非公共代碼中的Javadoc

Javadoc對于公共API非常有用蒿囤,但對于不打算做公共用途的代碼就令人厭惡了客们。為系統(tǒng)中的類和函數(shù)生成Javadoc頁并非總有用,而Javadoc注釋額外的形式要求幾乎等同于八股文章材诽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末底挫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脸侥,更是在濱河造成了極大的恐慌建邓,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睁枕,死亡現(xiàn)場離奇詭異官边,居然都是意外死亡沸手,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門注簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來契吉,“玉大人,你說我怎么就攤上這事诡渴【杈В” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵妄辩,是天一觀的道長惑灵。 經(jīng)常有香客問我,道長眼耀,這世上最難降的妖魔是什么英支? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮畔塔,結(jié)果婚禮上潭辈,老公的妹妹穿的比我還像新娘。我一直安慰自己澈吨,他們只是感情好把敢,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谅辣,像睡著了一般修赞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上桑阶,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天柏副,我揣著相機(jī)與錄音,去河邊找鬼蚣录。 笑死割择,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萎河。 我是一名探鬼主播荔泳,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼虐杯!你這毒婦竟也來了玛歌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤擎椰,失蹤者是張志新(化名)和其女友劉穎支子,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體达舒,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡值朋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年叹侄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吞歼。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圈膏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篙骡,到底是詐尸還是另有隱情稽坤,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布糯俗,位于F島的核電站尿褪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏得湘。R本人自食惡果不足惜杖玲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望淘正。 院中可真熱鬧摆马,春花似錦、人聲如沸鸿吆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惩淳。三九已至蕉毯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間思犁,已是汗流浹背代虾。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留激蹲,地道東北人棉磨。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像学辱,于是被迫代替她去往敵國和親含蓉。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容