可读代码编写炸鸡四 (下篇) - 提炼注释的下一步是提炼注释

用户头像
多选参数
关注
发布于: 2020 年 07 月 09 日
可读代码编写炸鸡四(下篇) - 提炼注释的下一步是提炼注释

大家好,我是多选参数的一员 —— 大炮。

上一篇炸鸡 中,我们大概了解了:

什么时候不该写注释,写注释可以怎么去写。

在上一篇结尾处,我们发现注释写出来后,是可以不断提炼的,所以注释存在一些优化方向和方法。

所以本篇炸鸡作为上一篇炸鸡的补充,提供一些这方面的建议。

这一篇与上一篇炸鸡统称为注释篇,而注释篇的行文思路如下图所示:

公众号后台回复 「注释」 获得源 pdf 文件

注释应该是怎么样的

在了解注释的优化方向前,我们需要了解一下注释应该是怎么样的。就好像你优化别人的代码,你就要提前画好图做好心理预期,告诉自己这段代码之后应该要是什么样的。

注释由于是占据屏幕空间的,所以太冗长的注释影响阅读,太短的注释又不能准确地提供信息

所以我们的一个小目标:

高的 (信息 / 空间) 率。也就是越小的空间输出越多的信息。

那么就需要注释:

  • 简练

  • 细致

  • 明确

如何优化

那我们现在已经了解了注释的小目标和应该的模样,那就可以冲着这几个点,提供几个方法。

为了简练

注释紧凑起来

如下代码的注释,就比较不紧凑。

-- 这是一个匹配函数
-- 但是只能匹配同一服务器上的用户
-- 同时实现了不连续匹配相同对手的功能
function match(uid)
...
end

三行变一行,单车变摩托。

-- 本服匹配,且用户不会连续匹配相同对手
function match(uid)
...
end

输入输出,永远的神

当然,有时候我们想表达一个功能,即使用简练的文字也不能很直观。

-- 将数组转为值为数组元素,值为 true 的 hash 表。
function array2map(array)
...
end

如果我们使用输入输出的例子来描述函数功能:

-- f({x, y, z, ...}) -> { [x] = true, [y] = true, [z] = true ... }
function array2map(array)
...
end

不过使用这个方法时,尽量选择 有普遍性 的例子,就好像如上的例子,若只有 f({x, y}) 这样的例子,容易产生 只允许两个元素的数组作为参数 的误导。

为了细致

声明代码的意图,而非仅是代码层面的意思

这个其实在 上一篇炸鸡 中已经提到了,我直接抄过来,不过分吧。

阅读者通过阅读代码能明白代码在 代码层面的功能,例如循环、查找指定字符等。但是不一定能很快了解这些代码 背后的意图,也就是 高级别的抽象

如下代码所示,代码层面的含义是 打乱 citys 数组,然后再循环这个数组,其中的元素不等于 lastCityId 则进入逻辑

local cityIds = random.randomArray(citys)
for _, cityId in ipairs(cityIds) do
if cityId ~= lastCityId then
...
end
end


但更高级别的抽象其实就是 从已知 citys 中随机选择一个与上次选择不一样的 cityId

所以利用注释只解释代码层面,往往是不够的。当阅读者了解代码层面的功能时,不一定能确认这段代码它的原作者意图到底是什么。

如果多了意图的解释,阅读者其实也能通过阅读来判断这段代码是否事与愿违,这其实也为代码多了一层 检查

润色粗糙用句

为了赶快,很可能就很潦草地写了一个注释。

-- 找一个合适元素

这个合适,什么算合适?怎么找?都没说。

我们可以润色且增加些信息:

-- 将 xxx 快排,找到最接近传参大小的 xxx 中的元素

精确描述函数行为

其实上一小点已经有了这一小点的味道了。我再举一个书中的例子:

-- 统计文件行数
function countLines(string)
end


这个统计行数看似讲明白了,但是有些细节没讲:

一行的定义是 \n 结尾的字符串?

还是 \r\n 结尾的字符串?

所以把这个细节考虑在内,就更精确了。

-- 利用 \n 统计统计行数
function countLines(string)
end

命名参数式注释

我们知道,python 是有命名参数的,这样可以为阅读者提供参数的一部分信息:

def xxx(first_param, sec_param):
passs

xxx(sec_param = 2, first_param = 1)

但有些语言并不支持这么做,例如我经常使用的 lua 语言。所以我们可以参考这种方式,在函数调用时这么写:

Hero.new(--[[configId = ]] 1, --[[heroId = ]] 2)

这样会更加细致。但是小心一点的是,尽量不要用在 可能高频修改的接口上,不然当接口修改,改个注释都要改半天。

这点我就被我的主程锤过。

为了明确

避免用代词

在书中提到了慎用 itthat 这样的代词。因为当主语变成两个,你可能不知道说的是谁:

-- insert the data into the cache, but check if it is too big

翻译一下的话:

-- 将数据写入缓存,但检查一下它是否数据过大。

所以到底是数据还是缓存会被检查呢。

记得维护同步

我记得听过一句名言

程序员最讨厌两件事,写文档和代码没有文档。

这对于注释来说也是一样的。

但是既然我们愿意写入注释了,当注释的相关代码出现变动,也要一并修改。

不然你的注释在第一层,你的代码已经在第五层,阅读者可能就在地下一层不知所措。

久而久之,很有可能导致你的团队里头的人直接忽略注释,他们开始害怕被注释误导。

这便事与愿违了。

总结

本篇炸鸡主要讲述了注释的优化方向

  • 越少空间提供越多信息

  • 简练、明确、细致

同时为了靠上这些要求,也提供了一些例子作为参考。

作为上一篇炸鸡的补充,本篇炸鸡就结束了。

写在最后,写给后头

可读代码编写炸鸡的第一层优化也告一段落了。

什么是第一层?我只在第一层,你已经第五层了对吧。

其实这段时间的炸鸡,都在于 命名、审美、注释 这些很表层的优化,而代码是逻辑的构成体,只是优化一些词句是很有限的。

所以在接下来的炸鸡,会开始着眼于代码逻辑上的优化,例如 if/else/elseiffor/while 等控制流的简化。

在最后还是感谢大家的阅读与支持

感谢大家支持可读代码编写炸鸡

感谢大家支持多选参数



发布于: 2020 年 07 月 09 日 阅读数: 105
用户头像

多选参数

关注

微信公众号:多选参数,不关注一波? 2018.09.20 加入

多选参数专注于各种技术的分享,目前专注于基础知识(数据结构与算法、操作系统)、通用技术(Git、MySQL、Redis、可读代码编写)、后端技术(Java)的分享。

评论

发布
暂无评论
可读代码编写炸鸡四(下篇) - 提炼注释的下一步是提炼注释