当前位置: 首页 > 科技观察

让我们一起回忆那些消失的代码注释

时间:2023-03-18 19:34:50 科技观察

今天和大家聊聊代码注释:为什么很多人不喜欢写注释?我应该写评论吗?我应该如何写评论?在现在的项目开发中,代码注释就像程序员的头发一样,越来越少。尤其是在中国,这种现象不仅在小公司和小团队中普遍存在,在大公司和大团队中的开源项目中也很普遍。上图是我在阿里的Druid项目源码中截取的。DruidDataSource是Druid大量使用的一个核心类,非常关键,但是即使对于这个关键的核心类,也没有任何评论。这张图来自阿里另一个著名的开源项目Dubbo。DubboProtocol是dubbo协议的实现类,dubbo协议是dubbo项目中最常见也是使用频率最高的默认协议,不做评论。没有注释给我们阅读代码时带来了很多不便。就像扔给你一个数码产品,上面密密麻麻的功能按钮,却没有给你说明书。那为什么代码注释消失了呢?原因我试着总结一下:1.国内程序员的职业环境对添加注释并不友好。在国内这种环境下,程序员每天都在压抑的996中挣扎,工作不停地做着,平时写代码很忙。添加注释进一步增加了工作量。没有人喜欢给自己增加工作量。想想看,费了九牛二虎之力写了一大堆代码,又经过反复自测和修改,终于调整好了,脑子里已经有些晕了。这个时候你会有多少想法来写这篇评论?什么?再想想,你可能想给代码加注释,突然这边的产品拉你开会,或者那边的运营告诉你需求变了,刚刚写的代码又要改了……此时,你还有为代码添加注释的想法吗?另外,写评论也需要很大的精力。一般来说,一个好的注释应该能够在有限的行数内说明:它注释的代码是干什么的,是一个什么样的概念,为什么要写这段代码。先不说写注释的麻烦,关键是注释还不算我们程序员的工作量。程序员的工作是实现业务程序。工作的结果不取决于你注释了多少代码,也不取决于你注释的好坏。只看你的程序有没有写完,有没有达到要求。上网会出什么问题。至于评论,是从程序员大哥的KPI里面滚出来的。有多少公司能像谷歌一样审查代码?一个BAT算一个,几乎没有意义。所以,国内程序员环境差是造成代码注释缺失的首要原因。2、查看注解的方式发生了变化Java是一门面向对象的语言。自诞生以来,业界不断为Java制定了无数的规范。2008年,《Clean Code》一书——中文名《代码整洁之道》——集这些规范于一身。《代码整洁之道》中有一个概念,注释是弥补代码表达能力不足的不得已的手段。如果代码能表达清楚,那就没必要写注释了。甚至,本书作者认为,写注释需要用失败二字来形容,也就是说,如果你写了注释,说明你的代码不够好,你写好代码的努力都失败了。这个理念也得到了很多业内大牛的认同。因此,越来越多的人认为,代码写得好,就不用写注释了。有空的话可以去看《代码整洁之道》的第4章,里面详细解释了现在业界很多人都接受的注解的概念。因此,“好的代码不需要注释”的观点也是造成注释少的原因之一。3、标注无规范,造成质量参差不齐。在许多团队中,没有注释规范。评论的方式和评论的位置没有任何规定,程序员可以自由发挥。这就麻烦了,一旦评论写出来,就很关键了。因为评论错了,比不写评论对人的伤害更大。如果注释写得不好,注释不仅起不到帮助阅读代码的作用,还可能影响代码的阅读,甚至把人带进坑里。如果没有评论规范,经常会出现有人做的好有人做的差的情况。比如有人到处加评论,i=i+1;//把i加1这么简单的代码还要加注释,有点放屁。有的人写了注释,但是需求变了,代码改了之后,注释也懒得改了。或者改代码的人不是原作者,新人连改完注释也要改都没意识到。所以,如果没有规范,很多程序员对注释没有正确的概念,注释写得不好,导致怨声载道……久而久之,就没人喜欢加注释了。我应该如何写评论?说了这么多不写评论的原因,这里也想说明一下我对评论的看法。我个人不同意《代码整洁之道》的评论观点。自己看评论好的代码,直接省了50%以上的功夫。评论好评的代码,读起来像常年脑血栓,皮棍一戳开,那叫一个顺。比如看Netty的注释:/***AnexustoanetworksocketoracomponentwhiscapableofI/O*operationssuchasread,write,connect,andbind.*

*Achannelprovidesauser:*

    *
  • thecurrentstateofthechannel(e.g.isitopen?isitconnected?),
  • *
  • 通道的{@linkplainChannelConfig配置参数}(例如接收缓冲区大小),
  • *
  • 通道支持的I/O操作(例如读、写、连接和绑定),以及
  • *
  • {@linkChannelPipeline}whichhandlesallI/Oeventsandrequests*associatedwiththechannel.
  • *
**

AllI/Ooperationsareasynchronous.

*

*AllI/OoperationsinNettyareasynchronous.ItmeansanyIthe/Ocallswill*returnimmediatelywiththenocomparanteeofquestedha相反,你会返回*a{@linkChannelFuture}实例,该实例将在请求的I/O*操作成功、失败或取消时通知您。**

频道是分层的

*

*一个{@linkChannel}可以有一个{@linkplain#parent()parent}取决于*它是如何创建的。例如,一个{@linkSocketChannel},被{@linkServerSocketChannel}接受*,将返回{@linkServerSocketChannel}*作为其父级{@link#parent()}。*

*层次结构的这些语义取决于{@linkChannel}所属的传输*实现。例如,您可以*编写一个新的{@linkChannel}实现来创建*共享套接字连接的子通道,如BEEP和*SSH。**

下播到访问传输特定操作

*

*一些传输公开了特定于*传输的额外操作。将{@linkChannel}向下转换为子类型以调用此类*操作。例如,使用旧的I/O数据报传输,多播*加入/离开操作由{@linkDatagramChannel}提供。**

Rel释放资源

*

*重要的是调用{@link#close()}或{@link#close(ChannelPromise)}释放所有*资源,因为您已完成{@linkChannel}。这确保所有资源*以适当方式发布,即文件句柄。*/publicinterfaceChannelextendsAttributeMap,ComparableChannel>{Channel是Netty中一个非常核心的接口。看评论就可以直接明白Netty为什么要创建Channel类,以及如何玩转Channel类。这些Netty注释告诉你清晰明了。所以,我觉得注释一定要有,但是要有标准,要有度。从实际的角度来看,我们团队有几个标准是必须要注解的:1.复杂的业务逻辑业务逻辑关联的东西太多或者步骤太多,或者两者都有,那么很少有人会去耐心仔细的阅读和一行一行理顺整个代码。这时,在业务逻辑实现的相关类中,需要明确说明业务逻辑实现中的类是什么,为什么要这样设计类,以及对应的业务逻辑。而且代码重构之后,注释也要重构。2.晦涩难懂的算法算法也要加注释,尤其是那些深奥的算法。不可能人人都是算法专家,一下子通过代码就明白算法实现的真谛。所以这里也加个注释,大致说明用的是什么算法,这个算法的出处或者相关文章的参考地址。3.非常规写作非常规写作往往有特殊情况,不得不做。例如,为了获得更好的性能;又如,为了修复一个bug,但又不想对代码做大的改动。总之,非常规的写法就是反模式、反套路,有时甚至会违背程序员的直觉。像这些做法一样,这种实现的原因必须写在注释中。4.可能有坑但是暂时没有好的解决办法。有时候,要求已经够难够复杂了,时间紧迫。你根本想不出一个特别好的解决方案来立即实现它。暂时只能想到一个简单粗暴的方案,先争取活下来。甚至有些地方为了先实现这个问题的需求,把一些变量的值写死了。像这样,很有可能在背后挖个坑。这时候注释必须加上为什么要这样解决,还必须加上//TODO,说明后面还需要进一步修改。5、关于项目的核心接口、类和字段在做项目时,需求中的很多核心概念很可能会映射到相应的接口或实体类上。如果在这些核心接口和实体类上加上清晰的注解,写下相应的业务概念,那么,在后期维护项目的时候,真的会事半功倍。比如在批处理调度系统中,我们可能会有多种任务概念,有的任务需要限制执行时间,有的任务不需要限制执行时间,那么在实现上,可能有一个LimitedTimeTask类对应限时任务,还有一个UnLimitedTimeTask类对应不需要限制执行时间的任务。那么这两个类一定要加注解,把对应的业务概念写清楚。如果一个具体的概念是复合的,由多个小概念组成,但是必须用一个接口或者一个类来表示,那么很可能要实现,必须要用字段来映射这些小概念,还必须加上这些字段注释解释相应的概念。总之,我个人理解一定要有评论,但是不能太泛泛,一定要有节制、规范地添加。最后说句直白的话,我对注释的态度是,就像写代码一样,一定要有规范。在这里跟店长说一句,想要大家写好评论,不能光靠“一定要写评论”这句高高在上的要求大家。没有规范,你不能完全责怪程序员不评论。最后,大刘提醒我,“四哥,注解规范里多了一项。”转载本文请联系思源外公众号。