50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

你写注释吗?写你就输了

  • 2020-07-20
  • 本文字数:5078 字

    阅读完需:约 17 分钟

你写注释吗?写你就输了

本文最初发布于 Level Up Coding 官方博客,经原作者授权由 InfoQ 中文站翻译并分享。


我并不是提倡不写代码注释,只是建议不要过于依赖注释,这样可以使代码更干净、更有表现力,这也能提高开发人员的水平。我自己也在寻求编写更简洁的代码,我尽力不编写糟糕的注释,并在可能时重构代码。



这篇文章的标题可能会让你情绪激动,但请先耐心听我说完。在适当的位置写下适当的注释可能非常有用,但是没有什么比无用的注释更让代码混乱了。在某些情况下,我敢说,注释可以弥补我们在代码中没有完全表达出来的意思。因此,写注释不值得赞美,而是应该停下来问问自己,是否有更好的方式可以用代码来表达自己。


带有少量注释的清晰而富于表现力的代码,要比带有大量注释的混乱而复杂的代码好得多。如果你已经把代码弄得一团糟,不要花时间写注释来解释,而是要花时间梳理代码。如果每次写注释的时候,你都冥思苦想,觉得自己的表达能力不足,那么最终你就会写出简洁明了的代码,完全没有必要写注释。鼓励自己用代码表达。

为什么对注释如此不屑?

因为它们会说谎,还会把代码弄得乱七八糟。虽说并非总是如此,也并非有意如此,但却经常如此。糟糕的代码和带有大量注释的代码之间有很高的相关性。注释存在的时间越久就越容易偏离它们所描述的代码——在某些情况下,它们可能是完全错误的。实际上,随着代码库和团队的增长,维护注释成了不可能的事情。


注释不同于《辛德勒的名单》。它们不是“纯善的”。事实上,注释充其量是一种必要的恶。——Robert C.Martin


当谈论关于注释的话题时,很重要的一点是,我们要看一下什么是恰当的注释,什么是糟糕的注释,这样我们才能学会写更好的注释,或者完全避免注释。

恰当的注释

并不是所有的注释都是不好的——有些注释实际上非常必要。

出于法律目的的注释

有时候,你可能需要出于法律目的编写特定的注释,比如开源项目的创作许可。一些现代化的 IDE 和文本编辑器会自动将它们折叠起来,保持工作区的整洁。

魔术表达式

如果你有一个复杂的 SQL 或正则表达式,它以神奇的方式做了一些令人兴奋的事,那么请务必注释,以便让读者更容易理解,因为我们都不是 Regex 忍者。


// 匹配电子邮件地址的正则表达式var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;return re.test(String(email).toLowerCase());// 注意:添加一个富于表现力的函数名,注释就变得没有必要了function validateEmail(email) {     var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;    return re.test(String(email).toLowerCase());}
复制代码

说明意图

在某些情况下,注释有助于解释决策或特定解决方案背后的意图。例如,测试套件中的一条注释告诉我们添加这行代码是为了降低死锁的几率。


for x in range(1, 500):    # 这是运行多个并行测试时预防死锁的最好方法  time.sleep(0.5)  runTest(x)
复制代码

结果预警

用注释说明代码可能会产生严重的或可怕的后果,甚至鼓励这样做。在本例中,开发人员让读者知道,当与回调函数一起使用时,QT 函数不是线程安全的。一般来说,如果一条注释可以避免某个人在编程时陷入绝望,那么它就是有用的。


"""  许多Qt函数都不是线程安全的。如果你使用回调函数,  即使你在所有绘制调用代码的周围都加上锁,你也会遇到段错误,  因为Qt的主事件循环仍在运行,并且使用了没加锁的资源。"""from multiprocessing.pool import ThreadPoolimport sysfrom threading import Lockimport timefrom PyQt5 import QtCore, QtWidgetsclass Task(QtCore.QObject):    updated = QtCore.pyqtSignal(int, int)    ...............    ...............
复制代码

TODO 注释

这些注释可以帮助我们标记那些我们认为应该做,但是由于某些原因没有做到的事情。它可能会提醒你删除废弃的特性,或者请求其他人查看某个问题。它可能是要求其他人想一个更好的名字,或者是提醒他们根据计划事件做出修改。


请记住,TODO 注释不是在系统中留下糟糕代码的借口。本质上,每一行代码都是一种负担——最安全、最快的代码是根本没有代码。


现在,大多数优秀的 IDE 都提供了特殊的指令和特性来定位所有的 TODO 注释,所以不太可能漏掉它们。尽管如此,你也不希望代码中到处都是 TODO。所以要经常浏览一下,删除那些你能删除的。

糟糕的注释

这个清单比较长,但在本节中,我们将看到一些更为老生常谈而又随处可见的注释。

明知故问的注释

有些注释的意思显而易见,即它们没有增加任何实际的价值,而且大多是噪音。


下面是一个开源项目的代码片段,其中包含大量明知故问型的注释,这些注释使代码变得混乱而晦涩。它们所提供的信息并不比代码本身多,而且在某些情况下,阅读注释的时间甚至比阅读代码长。


/*** 与该容器相关的集群*/protected Cluster cluster = null;/*** 人类可读的容器名 */protected String name = null;/*** 该容器的父容器*/protected Container parent = null;/*** 创建一个Loader配置父类加载器*/protected ClassLoader parentClassLoader = null;
复制代码

不清不楚的注释

如果你写注释是为了符合公司规定,或者你只是觉得有必要添加一些注释,那么你在注释时就不会进行适当的思考。所以,如果你真的写了一条注释,花点时间让它对阅读它的人有所帮助。


def load_config():  try:    do_useful_stuff()  except Exception as ex:     # 如有异常,退回到默认状态。
复制代码


在这个例子中,作者想要传达一些有关异常情况的重要信息。但这条注释没能解释清楚我们将退回到什么样的默认状态。如果一条注释要求我们转到另一个模块来找出默认值,那么它就没有发挥应有的作用。

注释掉代码

在团队准备好删除代码之前先将其注释掉似乎是一个好主意,但是不要这样做。注释代码是一种弊端,团队中的其他成员不会删除它,因为他们会认为它很重要。我们不是都在使用源码控制吗?所以我们不需要保留旧的代码。我们可以跳到任何我们想要的版本。

噪音注释

有些注释毫无意义,纯粹是噪音。时间久了,我们的大脑就会走马观花,我们也会开始跳过那些需要注意的重要注释。考虑一下下面的例子,其中的注释提供了很多价值吗?


-----------------------------# Exhibit A# 默认构造函数def get_todays_date():  return date.today()-----------------------------# Exhibit B# 返回月份的天# @return: 月份的天def get_day_of_month()  return day_of_month
复制代码


用编写干净代码的决心取代制造噪音的诱惑,你将成为一个更好、更快乐的程序员。

强制性注释

这肯定会引起争议。如果规定每个函数都需要一个 Java 文档或 Python docstring,是不是有点傻?大多数时候,类或函数名已经告诉我们注释所描述的内容,它们是多余的。在这个例子中,注释的数量比代码的数量还多——这让我很恼火。


class ComplexNumber:     """     这是一个用于复数的数学运算类。          属性:        real (int):复数的实部。        imag (int):复数的虚部。    """      def __init__(self, real, imag):         """         ComplexNumber类的构造函数。          参数:           real (int):复数的实部。           imag (int):复数的虚部。          """      def add(self, num):         """         该函数用于复数求和。          参数:            num (ComplexNumber):要加的复数。                  返回值:            ComplexNumber:包含和的复数。        """          re = self.real + num.real         im = self.imag + num.imag           return ComplexNumber(re, im)   help(ComplexNumber)  #访问类的docstring help(ComplexNumber.add)  # 访问方法的docstring 
复制代码

使用好的函数名或变量名

你可以使用更具表达性的函数和变量名替换注释,从而使代码更简洁。考虑下面的例子,第一个例子中的注释就变得没有必要了,因为有一个更好的函数名可以准确地告诉读者这个函数做了什么。


# 检查日期是否是过去的日期def check_date(date):   if date < today:     return true    return false 
def is_past_date(date): if date < today: return true return false
复制代码

注释不能弥补代码的糟糕

编写注释有一个比较常见的原因是糟糕的代码。我们以前都见过这种情况,在某种程度上,我们自己也犯过这样的错误。我们写一个模块或类,我们心里知道它混乱而无序。我们知道它一团糟。所以我们对自己说,“哦,我最好加下注释!”不!你最好把代码梳理清楚!


/* 这段代码糟透了。我知道,你知道,每个人都知道。我们假装什么都没发生,然后继续前进。以后你叫我白痴好了。*/
复制代码

小结

我并不是提倡不写代码注释,只是建议不要过于依赖注释,这样可以使代码更干净、更有表现力,这也能提高开发人员的水平。我自己也在寻求编写更简洁的代码,我尽力不编写糟糕的注释,并在可能时重构代码——将我的代码从宜家的一幅画变成梵高的作品。


所以让我们约法三章,不要写这么多注释。


原文链接:


Every time you comment code — you’ve already failed.


2020-07-20 13:503432

评论

发布
暂无评论
发现更多内容

腾讯T4整合Spring+Spring MVC+MyBatis+Redis实现

退休的汤姆

Java 程序员 面经 Java工程师 秋招

首次发布!Java面试八股文让569人成功进入大厂,堪称2022最强面试八股文核心知识版!

退休的汤姆

Java 程序员 面经 秋招 Java八股文

秋招国内大厂最牛的Java面试八股文合集(全彩版),不接受反驳

退休的汤姆

Java 程序员 面经 Java工程师 秋招

数据存储与物联网

CnosDB

IoT 时序数据库 开源社区 CnosDB infra

如果你是Java程序员,你会选择Cloud Studio进行云端开发,放弃IDEA吗?

wljslmz

Java Cloud Studio 9月月更

数据治理的内核:元数据管理

小鲸数据

数据治理 数字化 元数据 元数据管理 元数据管理平台

费时3个月啃烂了这份Redis技术笔记,我成功上岸进了字节

收到请回复

redis 架构 语言 & 开发 Java core redis 底层原理

DPDK技术学习路线总结,虚拟化专家之路

C++后台开发

后台开发 DPDK VPP OvS DPDK开发

阿里顶配版 Spring 全家桶高级笔记+300道硬核面试题,跪着啃完了

钟奕礼

Java 编程 程序员 架构 java面试

《游戏机图鉴》:发展、继承、崩溃、复兴,游戏机的前世今生

图灵社区

科普 游戏机

完美!华为大佬手码20w字Redis全栈小册,原来Redis性能可压榨到极致

Java全栈架构师

数据库 redis 程序员 面试 后端

如何成为资深的测试专家

穿过生命散发芬芳

测试 9月月更

软件复杂性的来源与应对

源字节1号

软件开发 前端开发 后端开发 小程序开发

C++后台开发学习路线(已多人拿下腾讯后台开发)

C++后台开发

后台开发 后端开发 C++后台开发 C++开发 腾讯后台开发

深入思考Schema管理的几个基本问题

HackMSF

什么是 SAP Business Function

汪子熙

SAP abap Netweaver 业务流程驱动 9月月更

C++学习------cerrno头文件的作用与源码学习

桑榆

c++ 9月月更

小六六学Netty系列之Java BIO

自然

网络 9月月更 neety

常见的网络安全攻击及防御技术概述

阿泽🧸

网络安全 9月月更

小六六学Netty系列之Java NIO(一)

自然

网络 9月月更 neety

设计模式的艺术 第十二章装饰设计模式练习(开发一个数据加密模块,可以对字符串进行加密。最简单的加密算法通过对字母移位来实现,同时提供了稍复杂的逆向输出加密和更高级的求模加密。用户先用最简单的算法加密,如果觉得不够,可以使用其他算法进行二次加密和三次加密)

代廉洁

设计模式的艺术

分布式技术难学?谷歌大神首发纯手撸ZK+Dubbo笔记,网友看完直呼NB

收到请回复

Java zookeeper 架构 分布式 语言 & 开发

重学网络系列之(我的名字叫IP)

自然

网络 9月月更

【大话 C 语言】春眠不觉晓,函数知多少?

Albert Edison

递归 C语言 函数 开发语言 9月月更

中小企业集成AI人工智能的窘境

felix

人工智能 中小企业 开放应用模型

设计模式的艺术 第十一章组合设计模式练习(开发一个界面控件库。界面控件分为两大类:一类是单元控件,例如按钮、文本框等;另一类是容器控件,例如窗体、中间面板等。试用组合模式设计该界面控件库)

代廉洁

设计模式的艺术

设计模式的艺术 第十三章外观设计模式练习(为新开发的智能手机控制与管理软件提供一键备份功能。通过该功能可以将原本存储在手机中的通讯录、短信、照片、歌曲等资料一次性地全部复制到移动存储介质(如MMC卡或SD卡)中。实现过程中需要与多个已有的类进行交互)

代廉洁

设计模式的艺术

在世界人工智能大会,看京东AI向产业奔涌

脑极体

Java工程师丨面试必会进程线程问答

陈橘又青

Java 面试 9月月更

你写注释吗?写你就输了_语言 & 开发_Tameem Iftikhar_InfoQ精选文章