坏味道——过多的注释(Comments)

特征

注释本身并不是坏事。但是常常有这样的情况:一段代码中出现长长的注释,而它之所以存在,是因为代码很糟糕。

问题原因

注释的作者意识到自己的代码不直观或不明显,所以想使用注释来说明自己的意图。这种情况下,注释就像是烂代码的除臭剂。

最好的注释是为函数或类起一个恰当的名字。

如果你觉得一个代码片段没有注释就无法理解,请先尝试重构,试着让所有注释都变得多余。

解决方法

  • 如果一个注释是为了解释一个复杂的表达式,可以运用 提炼变量(Extract Variable) 将表达式切分为易理解的子表达式。
  • 如果你需要通过注释来解释一段代码做了什么,请试试 提炼函数(Extract Method)
  • 如果函数已经被提炼,但仍需要注释函数做了什么,试试运用 函数改名(Rename Method) 来为函数起一个可以自解释的名字。
  • 如果需要对系统某状态进行断言,请运用 引入断言(Introduce Assertion)

收益

  • 代码变得更直观和明显。

何时忽略

注释有时候很有用:

  • 当解释为什么某事物要以特殊方式实现时。
  • 当解释某种复杂算法时。
  • 当你实在不知可以做些什么时。

重构方法说明

提炼变量(Extract Variable)

问题

你有个难以理解的表达式。

void renderBanner() {
  if ((platform.toUpperCase().indexOf("MAC") > -1) &&
       (browser.toUpperCase().indexOf("IE") > -1) &&
        wasInitialized() && resize > 0 )
  {
    // do something
  }
}

解决

将表达式的结果或它的子表达式的结果用不言自明的变量来替代。

void renderBanner() {
  final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
  final boolean isIE = browser.toUpperCase().indexOf("IE") > -1;
  final boolean wasResized = resize > 0;

  if (isMacOs && isIE && wasInitialized() && wasResized) {
    // do something
  }
}

提炼函数(Extract Method)

问题

你有一段代码可以组织在一起。

void printOwing() {
  printBanner();

  //print details
  System.out.println("name: " + name);
  System.out.println("amount: " + getOutstanding());
}

解决

移动这段代码到一个新的函数中,使用函数的调用来替代老代码。

void printOwing() {
  printBanner();
  printDetails(getOutstanding());
}

void printDetails(double outstanding) {
  System.out.println("name: " + name);
  System.out.println("amount: " + outstanding);
}

函数改名(Rename Method)

问题

函数的名称未能恰当的揭示函数的用途。

class Person {
  public String getsnm();
}

解决

修改函数名。

class Person {
  public String getSecondName();
}

引入断言(Introduce Assertion)

问题

某一段代码需要对程序状态做出某种假设。

double getExpenseLimit() {
  // should have either expense limit or a primary project
  return (expenseLimit != NULL_EXPENSE) ?
    expenseLimit:
    primaryProject.getMemberExpenseLimit();
}

解决

以断言明确表现这种假设。

double getExpenseLimit() {
  Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);

  return (expenseLimit != NULL_EXPENSE) ?
    expenseLimit:
    primaryProject.getMemberExpenseLimit();
}

注:请不要滥用断言。不要使用它来检查”应该为真“的条件,只能使用它来检查“一定必须为真”的条件。实际上,断言更多是用于自我检测代码的一种手段。在产品真正交付时,往往都会消除所有断言。


后记

一些朋友对我这篇文字有很多质疑。
在这里,我想集中再强调一下我的观点,避免让大家有歧义。
注释的作用,很多时候,非常类似于马路上的警示牌:“前方有坑,悠着点!”难道因为有了警示牌,就不用把坑给填上了吗?但是,在坑没有填上之前,把警示牌给撤了或者压根就没想要立个警示牌,这就更加不应该了。
不知我这么说,大家是否更容易理解。

引申阅读

欢迎继续阅读 代码的症与药 系列文章。

代码的坏味道(13)——过多的注释(Comments)的更多相关文章

  1. 【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  2. Bad Smell (代码的坏味道)

    sourcemaking 如果一段代码是不稳定或者有一些潜在问题的,那么代码往往会包含一些明显的痕迹.正如食物要腐坏之前,经常会发出一些异味一样, 我们管这些痕迹叫做 "代码异味" ...

  3. 重构 之 总结代码的坏味道 Bad Smell (一) 重复代码 过长函数 过大的类 过长参数列 发散式变化 霰弹式修改

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  4. Chapter 3 :代码的坏味道

    "如果尿布臭了,就换掉它." --Beck奶奶,论保持小孩清洁的哲学 代码的坏味道这一章集中论述该何时重构.具体的重构方法在后面的章节. "没有任何度量规矩比得上见识广博 ...

  5. Refactoring之——代码的坏味道(一)过长方法

    1 代码的坏味道 重构一书中提到了22种代码的坏味道,大致可以分为几类. 识别代码的坏味道,有助于发现代码的潜在问题,从而可以有的放矢的修改现有代码,使之不断完善. 1.1 Bloaters(臭鲱,暂 ...

  6. 消灭 Java 代码的“坏味道”

    消灭 Java 代码的“坏味道” 原创: 王超 阿里巴巴中间件 昨天 导读 明代王阳明先生在<传习录>谈为学之道时说: 私欲日生,如地上尘,一日不扫,便又有一层.着实用功,便见道无终穷,愈 ...

  7. 【转】Bad Smell(代码的坏味道)

    1.Duplicated Code(重复的代码) 臭味行列中首当其冲的就是Duplicated Code.如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好. ...

  8. 解析大型.NET ERP系统 代码的坏味道

    1  对用户输入做过多的约定和假设 配置文件App.config中有一个设定报表路径的配置节: <add key="ReportPath" value="C:\Us ...

  9. 代码的坏味道(21)——中间人(Middle Man)

    坏味道--中间人(Middle Man) 特征 如果一个类的作用仅仅是指向另一个类的委托,为什么要存在呢? 问题原因 对象的基本特征之一就是封装:对外部世界隐藏其内部细节.封装往往伴随委托.但是人们可 ...

随机推荐

  1. 记一次SQLServer的分页优化兼谈谈使用Row_Number()分页存在的问题

    最近有项目反应,在服务器CPU使用较高的时候,我们的事件查询页面非常的慢,查询几条记录竟然要4分钟甚至更长,而且在翻第二页的时候也是要这么多的时间,这肯定是不能接受的,也是让现场用SQLServerP ...

  2. Minor【 PHP框架】1.简介

    1.1 Minor是什么 Minor是一个简单但是优秀的符合PSR4的PHP框架,It just did what a framework should do. 只做一个框架应该做的,简单而又强大! ...

  3. react入门参考资料--step by step

    ReactJS简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出 ...

  4. vue.js初探

    前言 入手2016最火前端框架之一vue.js.大概从网上找了些资料看了下vue.js,从网上的资料来看只能惊叹其发展速度太快,让我意外的是其作者是华人的前提下作品这么受欢迎. 网上的博客和教程各种组 ...

  5. web api接口同步和异步的问题

    一般来说,如果一个api 接口带上Task和 async 一般就算得上是异步api接口了. 如果我想使用异步api接口,一般的动机是我在我的方法里面可能使用Task.Run 进行异步的去处理一个耗时的 ...

  6. c++ pair 使用

    1. 包含头文件: #include <utility> 2. pair 的操作: pair<T1,T2> p; pair<T1,T2> p(v1,v2); pai ...

  7. Spring异步功能

    使用 Spring 的异步功能时,实质是使用的 Servlet3 及以上版本的异步功能. Spring 的异步处理机制需要在 web.xml 中全部的 servlet 和 filter 处配置 < ...

  8. Android快乐贪吃蛇游戏实战项目开发教程-06虚拟方向键(五)绘制方向键箭头

    本系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html本系列教程项目源码GitHub地址:https://github.com/jack ...

  9. 《Note --- Unreal 4 --- Sample analyze --- StrategyGame(continue...)》

    ---------------------------------------------------------------------------------------------------- ...

  10. Android(3)—Mono For Android App版本自动更新(2)

    0.前言 这篇博文是上一篇的延续,主要是修改上一个版中的BUG和优化一些待完善的项,也算是结贴,当然还有需要完善的,等日后项目中用到的时候再单独写出来吧,本篇主要写升级改进的部分: 改进1.修复[BU ...