请停止代码注释

“干净的代码应该像写好的散文一样” - Robert C. Martin

不良代码的通病就是有很多注释。这是凌乱的源代码最明显的迹象。

每个程序员的目标应该是编写干净和富有表现力的代码,以避免代码注释。每个变量,函数和类的目的应该隐含在其名称和结构中。

当其他人读取您的代码时,他们不应该阅读注释以了解你的代码正在做什么。命名良好的类和函数应该引导读者通过你的代码,就像一本写得很好的小说一样。当读者看到一个新的类或功能时,他们不应该对他们在里面看到的东西感到困惑难以理解。

请记住,开发人员的工作时间很少花在编写代码上,花在阅读代码和理解代码上的时间要多得多

注释掩盖自身的失败

我经常看到变量或函数名称之上的注释描述代码执行(或应该执行的操作)的内容。这些注释给我的感受就是,这段代码的前任眉有办法想象一个富有表现力的名称,或者他们的功能不仅仅是一件事。

在代码中命名是非常重要的。您应该花费大量精力准确而精确地命名每一段代码,以便其他开发人员能够理解您的代码。

// 按状态查找员工
List<Employee> find(Status status) {
  ...
}

在此示例中,名称find不够描述,因此此函数的作者需要留下描述函数功能的描述性注释。当我们看到从另一个模块调用的find函数时,它的作用是一个谜。它发现了什么?究竟是什么意思?它返回了它发现的东西吗?怎么找到它发现的东西?就像鲍勃叔叔在他的书《Clean Code》中所说,如果你需要写注释,你就无法通过代码表达自己真实的用意。

我们不希望检查每个函数上面的注释,以了解它的作用。

List<Employee> getEmployeesByStatus(Status status) {
  ...
}

现在很明显能看出来这个函数的具体作用,这使得注释变得多余。这让我想到了注释失败的下一个方式。

冗余注释

这些混乱了你的代码,完全没必要。添加许多冗余注释会引导读者形成这些注释都很“无聊”的心态,从而跳过每条注释,因此当有重要注释时,也不会阅读。

//此函数发送电子邮件
void sendEmail() {
  ...
}

//此函数发送电子邮件
public class Employee {
  ...
}

/ **
* @param title CD的标题
* @param作者CD的作者
* @param track CD上的曲目数
 * /
public void addCd(String title, String author, int tracks) {
  ...
}

多数情况是强制冗余。很多公司在每个功能和类别上都要求这一点。如果你的上司要求这样做,emm你可以尝试请他们不要。

错误的抽象程度

如果您有一个很长的功能或需要记录代码的哪一部分做了什么,那么您可能违反了这些规则:

  1. 功能应该做一件事。
  2. 功能应该很小。

这是一个例子

//此函数计算价格,与销售额进行比较
//促销,检查价格是否有效,然后
//向用户发送促销电子邮件
public  void doSomeThings(){

  //计算价格
  ...
    ...
    ...
  
  //将计算出的价格与促销活动进
  ...
    ...
    ...
  
  //检查计算的价格是否有效
  ...
    ...
    ...
  
  //向用户发送促销信息
  ...
    ...
    ...
}

当你成功地将逻辑的每个部分封装到一个单独的函数中时,代码不需要注释就会表现的应该像它的作用描述一样。

重构如下:

public  void sendPromotionEmailToUsers(){
  calculatePrices();
  compareCalculatedPricesWithSalesPromotions();
  checkIfCalculatedPricesAreValid();
  sendPromotionEmail();
}

而不是注释代码的每个部分,每个逻辑块应该很好地封装在它自己的函数中。

首先,这提高了可读性。每个代码块不必逐行读取。我们可以简单地读取辅助函数名称并理解它的作用。如果我们想要了解每个函数内部的更多细节,就能去看具体实现。

其次,它提高了可测试性。在上面的示例中,我们可以为每个函数单独进行单元测试。如果不封装这些单独的函数,则很难测试较大函数sendPromotionEmailToUsers()的每个部分。执行多个功能的功能很难测试。

最后,它提高了可重构性。通过将逻辑的每个部分封装到自己的函数中,将来更改维护更容易,并且单独功能的函数会被隔离以仅更改该函数的行为。当我们使用局部变量的长函数在整个函数中持续存在时,由于函数的紧耦合,很难在不导致其他地方变化的情况下重构函数。

注释掉的代码

注释掉的代码应该被视为roadkill。不要看它,不要闻它,不要问它从哪里来,只是摆脱它。保持它的时间越长,其余代码闻到的时间就越长......

/ *
public void oldFunction(){
  noOneRemembersWhyIAmHere();
  tryToUnCommentMe();
  iWillProbablyCauseABuildFailure();
  HAHAHA();
}
* /

尽管删你不删别人更不敢删。如果你以后需要它,你可以随时检查版本控制系统,因为你肯定用了VCS,对吗?(如果不是当我没说)

TODO注释

不要写TODO注释,而不仅仅是......做到了吗?大多数时候这些注释都会被遗忘,后来可能变得无关或错误。当另一个程序员稍后看到TODO注释时,他们如何知道是否还需要这样做?

不过偶尔TODO注释是好的,如果你正在等待另一个队友的合并(一般不会太久)。就可以这么做,直到你可以进行修复并提交它。

“当你觉得有必要写评论时,首先要尝试重构代码,以便任何评论都变得多余。” - Martin Fowler

注释的谎言

当Jimmy在他写的新功能上面打上注释时,他认为他正在帮助任何看到他的代码的未来开发人员。其实呢他真正在做的是设置一个陷阱。他的注释可能是弥天大谎(没有双关语意图)蛰伏数月或数年没有被触及,只是等待成为一个令人讨厌的陷阱。然后有一天,在数百个重构和需求变更之一中,他的注释从一些遥远的模块中失效,但是仍然在错误的引导着无数的接盘侠。

当你更改一行代码时,你怎么知道你更改的代码会不会使其他地方的注释无效?没有办法知道。注释必须毁灭

public class User {
  ...
  //它包含用户的名字和姓氏
  String name;
  ...
}

然后,需求更改,他们希望将名称拆分为firstName和lastName。

public class User {
  ...
  
  // 它包含用户的名字和姓氏
  String firstName;
  String lastName;
    
  ...
}

注释现在已经错了。你可以更新注释以反映更改,但是你是否真的想在每次更改后手动维护所有注释?你是开发人员,而不是文档。

但是这个注释很容易被注意到并且没有问题需要改变。但是你很难保证在程序的其他地方,会不会也注释了这个参数name是用户的名字和姓氏。更改一小块地方的代码,可能会让很多的代码注释都失效。

让我们看另一个例子:

//根据状态处理员工
void processEmployees(){
  ...
  List < Employee > employees = findEmployees(statusList);
  ...
}

//这会按状态列表查找Employees
List < Employee > findEmployees(List < String > statusList){
  ...
}

然后有人被要求更改函数findEmployees,以便通过名称列表而不是状态列表查找员工。

//根据状态处理员工
void processEmployees(){
  ...
  List < Employee > employees = findEmployees(statusList);
  ...
}

//这会按状态列表查找Employees
List < Employee > findEmployees(List < String > nameList){
  ...
}

首先,上面的注释findEmployees已经失效,因此需要更改。没问题,对吧?错了

processEmployees上面的注释也已失效,因此也需要更改。还有多少其他评论被这个小型重构改成无效?这一次更改在源代码中创建了多少注释谎言?

替代方案:

void processEmployees(){
  ...
  List < Employee > employees = findEmployeesByName(nameList);
  ...
}

List < Employee > findEmployeesByName(List < Name > nameList){
  ...
}

如果你准确而准确地命名你的函数,则不需要注释,并且你不会在代码中散布谎言。

“代码永远不会说谎,注释会。” - 罗恩杰弗里斯

什么时候需要注释呢

我知道很多开发人员都是代码注释的死硬支持者,对他们来说,我必须承认有时注释是可以的。不过你每写一段都应当有充足的理由

复杂表达式

如果您有复杂的SQL或正则表达式语句,请继续编写注释。在代码中干净利落地表达诸如此类的陈述可能很困难。在这些表达式上面添加注释可以帮助其他开发人员更好地理解您的代码。

// 格式匹配kk:mm:ss EEE,MMM dd,yyy
Pattern timePattern = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w*, \\d*, \\d*");

注释警告

如果你需要警告其他开发人员这段代码可能发生的bug,可以在此代码附近留下注释。这些注释可以充当代码中神秘行为的先兆,并为你的代码增加价值。

意图澄清

如果你实在命名废,那就要承担你的失败并写下注释。你负责编写的代码,所以永远不要把编写得不好的代码留下来。

如果你必须撰写注释,请确保它是本地的。远离其引用的非本地评论注定会失效并变成谎言。引用函数或变量的注释应直接位于其上方。警告注释可以在它引用的代码的上方或旁边。如果您的IDE支持注释突出显示,请使您的警告注释从其余代码中脱颖而出。

最后

我已经建立了对代码注释的感受。我鄙视他们,但我知道有时他们是需要的。

所以,请停止写这么多注释。

本文是作者在推特上看到国外一位大神 布莱恩·诺兰德 的论述,深以为然因此翻译后加以修饰进行分享的。希望今后自己的代码也能像散文一样优雅。

你的代码怎么不写注释
”不好意思,我的代码就是不用写注释“ (逃

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 一、整洁代码 A.混乱的代价 1.有些团队在项目初期进展迅速,但有那么一两年的时间却慢去蜗行。对代码的每次修改都影...
    ZyBlog阅读 2,012评论 0 2
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,400评论 1 45
  • 从百度文库下载下来的,这里保存一份 别人的原代码程序员怎样阅读 源码就是指编写的最原始程序的代码。 运行的软件是要...
    Albert陈凯阅读 3,369评论 0 15
  • 见 想见的人做 想做的事挣 能挣的钱然后成为更好的自己 这世界变得太快大家都喜欢无所谓即便假装
    Xici大猫阅读 117评论 0 0
  • 阅读前请关注“实战管理学”,就可以每天获取本头条号发布的职场及管理知识!实战管理学,坚持无干货,不分享! 世界上最...
    实战管理学阅读 372评论 0 0