前言
最令程序员头痛的事情莫过于阅读别人的代码,但其实时间一久阅读自己的代码也会很痛苦。 问题不是出在『自己或别人』,而是在代码本身。
必要的注释可以阐明实现细节和设计意图,以此节约自己和别人的时间。 然而很多时候注释起的作用却适得其反,比如自动生成的过多的注释分散阅读者的注意力, 而过期的失效的注释更是误导阅读者。
自动生成的注释
代码注释的泛滥想必是从Eclipse,Visual Studio等IDE开始的。 这些IDE提供了很多快捷功能,生成类/接口的骨架,具有Getter/Setter的属性等等。 如果用过IDE,下面的代码你一定不会陌生:
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub }
上述6行代码中的4行注释包含的信息量是0,既没有阐释参数args是何物,也没有说明main的用途。 然而大量的项目中都充斥着这样的自动生成注释。
『建议』:如果有参数或机制需要说明,请补充这些信息。否则请删除自动生成注释。 当然,用于生成文档的注释除外。
过多的注释
总会有人不厌其烦地编写长篇累牍的注释,或无微不至,或语焉不详,或晦涩难懂,或文采飞扬。 总之没有帮助我更快阅读代码的注释都是失败的注释。
为了说明问题,Harttle克隆了4.x Linux Kernel源码, 来大致分析一下其注释行数。 我们知道内核代码95%以上是C语言,所以统计.c文件就足够说明问题了。
➜ linux git:(master) git clone git@github.com:torvalds/linux.git --depth=1 ➜ linux git:(master) find . -name "*.c" -o -name "*.h" -exec grep -E '^\s*((\*)|(/[/*]))' {} \; | wc -l 724804 ➜ linux git:(master) find . -name "*.c" -o -name "*.h" -exec cat {} \; | wc -l 4018961 ➜ linux git:(master) node > 724804/(4018961-724804) 0.22002715717556875
内核仓库中的代码大概是402万行(未移除空行),其中注释72万行,占比22%。 Linux内核使用低级的C语言编写,涉及到复杂的CPU调度、内存管理,驱动程序。 因此注释会偏多一些,一般的项目注释应小于这个数值。
『建议』:如果你的代码中注释超过了20%,那么显然你过度注释了。
文件头注释
很多编辑器/IDE都会生成默认的文件头,例如:
/** * @file /tmp/xxx.js * @author harttle(yangjvn@126.com) * @date 2016-08-30 22:33 * @description A XXX Implementation for XXX. */
文件头注释清晰地列出了文件的作者、功能描述等信息,看起来很有用。 不过这样的文件头存在的问题在于其维护性:
文件头注释意在维护代码文件的元信息,以便在分发和部署过程中维护作者版权等信息。 然而在拥有版本控制的代码仓库中,这些信息不再需要手动维护,甚至可以通过git blame查看每一行代码的作者和时间信息。
『建议』:使用版本控制工具,删除文件头注释。版权信息可在构建或分发时生成。
冗余的注释
意图非常清楚的代码原则上不需要注释,多余的注释反而会造成维护性问题。 尤其是非英语母语的作者常常会掉到这个坑里。比如变量和函数的注释:
/* * 获取用户数目 */ function getUserCount(){ // 用户的列表 var userList = []; }
这不是废话么!冗余的注释问题仍然在于维护性,例如调整函数功能、调整参数顺序, 或者更换变量名时我们不得不更新这些注释。否则这些注释就会误导下一个读者。
【建议】:不说废话。
抽取注释到标识符
可能读者也会有这样的经验:当我们写了一大段代码时,往往需要把它们分为几块。 然后在每一块开头添加一段注释。例如:
function calcTotalCharge(movies, user){ // Calculate Movie Charge var movieCharge = 0; for(var i=0; i<movies.length; i++){ var charge = 0; if(movie.type === 'discount'){ charge = movie.charge * 0.8; } else if(movie.type === 'short'){ charge = movie.charge * 2; } else if(movie.type === 'normal'){ charge = movie.charge; } movieCharge += charge; } // Calculate User Charge var rentCharge = 0; if(user.isVIP1){ rentCharge = 10; } if(user.isVIP2){ rentCharge = 200; } else if(user.isVIP3){ rentCharge = 300; } else if(user.isVIP4){ rentCharge = 500; } // Calculate Total Charge return movieCharge + rentCharge; }
上述代码中的三段注释确实加速了阅读代码的速度, 但每当代码需要注释才能读懂时就应该警醒:是不是结构设计有问题。 对于上述代码,我们可以通过更加可复用的结构来消除注释:
function calcTotalCharge(movies, user){ return calcMovieCharge(movies) + calcUserCharge(user); } function calcMovieCharge(movies){ var total = 0; for(var i=0; i<movies.length; i++){ total += calcSingleMovieCharge(movie); } return total; } function calcSingleMovieCharge(movie){ if(movie.type === 'discount') return movie.charge * 0.8; else if(movie.type === 'short') return movie.charge * 2; else if(movie.type === 'normal') return movie.charge; return 0; } function calcUserCharge(user){ if(user.isVIP1) return 10; else if(user.isVIP2) return 200; else if(user.isVIP3) return 300; else if(user.isVIP4) return 500; return 0; }
代码重构之后原来的注释就变得毫无意义,代码意图都被清晰的表述在标识符的命名中。 通常重构会带来代码量的减小,因为封装了分支、每个单元的逻辑也更加明确。
【建议】:当我们发现不得不进行注释时,需要警醒是否结构设计发生了问题。
有用的注释
至此Harttle已描述了这么多反模式,并非为了说明代码注释不重要。 而是为了说明『代码注释存在的意义在于帮助理解代码本身』。 例如在编写一些Trick,Polyfill,临时代码,以及复杂算法时,注释变得相当重要。 例如:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
关于使用强化代码注释的名称和语法,我有一个问题。 简短、简短、非常简短的版本是:我正在寻找一个指南/手册,它将列出可用的代码内注释,并为它们的预期用途提供一个示例。专门用于从Fortify扫描结果中省略一组“SQL注入”和“SQL注入:持久性”问题。 TL;博士;细节: 强化版:19.1.0 平台:Java 8;Web App(Weblogic 12.2.1.4.0(12c));Redhat Un
我正在使用java中的Webdriver示例和selenium来交互网页的元素。我需要在Java中执行对webdriver示例中的javascript函数的调用。他们要求我通过使用javascript函数来标识用户,该函数是: Dtrum.IdentifyUser(tagValue) 基于以下内容:https://www.dynatrace.com/news/blog/accelerating-f
问题内容: 是否可以注释代码块?例如循环还是大括号?如果是这样,怎么办? 第一.java ForCycle.java 根据http://www.javacodegeeks.com/2012/11/java-annotations-tutorial-with-custom- annotation.html @Target –指示注释类型适用的程序元素的种类。一些可能的值是TYPE,METHOD,CO
我是否正确理解了使用SBT的最简单方法是使用OSGI插件将工具包安装到Domino服务器中,或者使用捆绑的Tomcat服务器? 在生产中使用代码段的正确方法是什么?仍然希望它引用Playerd数据库?
我认为代码(如下)已经优化(只需使用比相同逻辑的初始版本更少的变量) > 在优化过程中,我应该考虑哪些因素? 这是代码(也在jsfiddle上) 这是代码的解释。“处理”函数在数组中查找相同的值,对于每个相同的值,它通过将一个数字挂起到该值来更改值,“数字”表示它在数组中找到的值的计数。 例如arr=["x","x","y","z"]将返回["x(1)","x(2)","y","z"]"y"和"z
嗨,我想使用comment将属性和函数分开。 如何在代码中注释像绿线一样实现?