写点什么

可读代码编写炸鸡四 (上篇) - 来写注释

用户头像
多选参数
关注
发布于: 2020 年 06 月 30 日

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

从上一篇开始,我们针对代码可读性的优化范围从

一个变量,一个函数名

扩大到了

多行代码,多个函数

而上一篇炸鸡 审美 中,将这个范围下的优化又分成了两个部分

  • 代码审美

  • 代码注释

所以在讲完代码审美之后,自然而然这一篇炸鸡要讲一讲 代码注释 的可读性优化。

同时上篇炸鸡抛出了一个问题。在本篇炸鸡,应该能找到答案。

关于注释的炸鸡的行文思路如下图所示。

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

什么时候不需要注释

一般你加入了一个项目组,领你进来的人通常会帮助你熟悉环境,而你可能记得最清楚的便是

xxx 是不能做的,你要注意。

所以在写注释前,先要注意一下什么时候 不需要注释

1. 代码本身能让阅读者快速判断

我举个最极端的例子:

-- 这是生成一个英雄实例
local hero = Hero.new()

很明显,这样的代码阅读者能 快速 判断出其功能和意图,注释就是多此一举,为了注释而注释。

所以这时候应该能理解 上一篇炸鸡 抛出的问题了吧?

当然如果遇到这样的一行代码,那么注释是有必要的。

# 删除第二个 ? 之后的所有内容
name = '?'.join(line.split('?')[:2])

因为虽然阅读者可以通过一定时间的阅读可以得出结果,但是这种 一行嵌套多个方法 的代码可读性已经下降,所以加上注释能帮助阅读者快速理解代码。

当然,这样的代码,也可以利用临时变量将嵌套的函数结果暂存,同时对临时变量恰当命名,也能达到一定的效果,而这个方面的内容在之后的炸鸡会涉及,本篇就不再赘述。

2. 注释没有提供更多的信息

如果只是对一个函数这样的声明注释,那么大可以删去。

-- 结束采集,在采集部队结束时调用
function endGather()
...
end

亦或者提供更多的信息:

-- 结束采集,在采集部队结束时调用
-- 返回采集初始时间,经 buff 调整时间,奖励列表等。
function endGather()
...
end



-- 结束采集,在采集部队结束时调用
-- 利用快排将采集时间与收益按照顺序一一映射,可快速索引最终收益
function endGather()
...
end

当然,这个已经是下文的内容了。

3. 命名不好

如果你有稍微了解过我之前的几篇关于代码命名的炸鸡,可以理解好的命名已经能提供不少信息。

如下代码所示,myFind 是个不明白其含义的函数命名。

-- 查找字符中第二个指定字符。
funciton myFind(char)
end

看了注释才明白这个函数的作用,可是如果直接将函数名换成 find2ndAppointChar,也许会更恰当,而且还省去了注释。

我们可以得到一个不等式:

好的命名 > 坏命名 + 注释

确定为代码写上注释

那么我们已经明确了什么时候不需要注释,那么接下来就需要知道确定要写注释的时候,该怎么写。

如何写注释

1. 记录思想

当我们写注释的时候,可以把当时写这段代码的动机意图,所思所想写下来。大致可以分成两点:

  • 记录内在逻辑算法。

  • 承认代码的瑕疵。

我们利用注释可以记录一段代码的 内在逻辑算法,我们拿前文的例子来说明即可。

-- 结束采集,在采集部队结束时调用
-- 利用快排将采集时间与收益按照顺序一一映射,可快速索引最终收益
function endGather()
...
end

当然,我们的代码总会因为一些客观原因并不能尽善尽美,所以完全可以利用注释 提出这段代码的瑕疵

-- todo: 本函数存在死循环可能,需要优化。
function randomChooseCity(cantChooseCity)
while true do
local cityId = math.random(1, 20)
if cityId ~= cantChooseCity then
return cityId
end
end
end

上述代码我们可以注意到,todo 这样的标签,利用这样的标签可以告诉阅读者关于这段代码的瑕疵。

所以我们可以看到,利用注释,阅读者便能很快地了解你当时的想法和企图,而且这个阅读者很有可能就是未来几个月后的你。

这里插一嘴,如果你用 vscode 编写代码,推荐一个还不错的注释插件 Better Commments

利用如图方式提供较为好辨认的提示

2. 提供维护和修改的指导思路

你的代码不一定永远都是你来维护,就好像我之前一段时间接手了好几个别人的写了一半的代码。所以为了方便后来者的维护,我们可以利用注释提供修改思路,减轻后来者的负担。

(当然,你要直接找作者也可以,毕竟算个活注释。)

承认代码瑕疵

在上一点,我们知道了可以通过注释承认代码的瑕疵来记录当时的思想,而承认代码的瑕疵便是为 后来者提供了改进思路

常量注释

想必各位写代码的时候或多或少的会用到常量。其实很多情况下常量的命名就已经提供了足够的信息了:

ss.const.MAX_ITEM_AMOUNT = 10

那么如果为常量写上注释,那一定是要提供额外的信息,这便是 解释常量值的计算公式 或者 常量值由来与调整范围

-- 最大 item 数可接受 5 - 20 之间调整。
-- MAX_ITEM_AMOUNT = log(MAX_SHOP_AMOUNT) / STD_KINGDOM_AMOUNT (4 - 7)
ss.const.MAX_ITEM_AMOUNT = 10

3. 站在阅读者角度

其实写注释就是为了让阅读者方便理解代码,所以写注释一定要站在阅读者的角度。

主要可以分成三个情况:

  • 容易产生疑惑点的解释

  • 实现细节的公布

  • 高级别的抽象解释

前两个情况,其实就是 记录思想提供维护和修改指导思路 两个点涉及的内容。这里主要讲一下第三个情况。

高级别的抽象解释

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

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

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

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

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

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

4. 调整心态,尝试着写出来

很多时候,我个人也很抵触写注释,写代码累个半死还要再打字写注释,想下手的时候又不知从何开始。

所以写注释前,调整心态还是挺重要的。我们可以尝试以下三步走,来写自己的注释:

  • 先写,不管多啰嗦

  • 看看有没有可以提炼的

  • 不断提炼

我举个栗子,我写了如下的函数和注释。只是为了记录所想。虽然长的要命,但还是写出来了。

-- * 这是在每天 0 点的时候执行的函数
-- ! 为离线玩家补偿, 应该拿 logoutTime 来计算,不应该再多一个 lastDayPassTime
-- ! 但是,在线跨好几天的玩家不能走同一套逻辑, 通过 isTimerTrigger 来区分是手动调用还是 daypass 服务调用
function onDayPassMakeUp(isTimerTrigger)
...
end

然后可以考虑能不能提炼一些内容,因为命名已经是叫做 onDayPassMakeUp, 命名已经告诉我们一些信息,所以可以删去第一行注释。同时解释内在补偿逻辑时,也可以省一些字句。

-- ! 针对离线玩家,用 logoutTime 计算未参加活动的天数
-- ! isTimerTrigger 区分在线跨天和离线跨天。
function onDayPassMakeUp(isTimerTrigger)
...
end

这样算是一次提炼了。

不难发现,本篇炸鸡其实主要侧重让编写者能够开始写注释同时确定该怎么写,但是在这一小节我们看到,写出的注释不一定是最恰当的,是存在 优化方向的

所以下一篇炸鸡就稍微说一说关于 注释的优化方向和优化方法,希望对你有所帮助。

总结

  • 确定了什么时候不该写注释

  • 代码坏味道命名

  • 代码本身能被快速判断功能

  • 注释不能提供更多信息

  • 提供了一些写注释的思路与方法:

  • 记录思想,例如记录函数算法逻辑或者承认代码的瑕疵;还有 todofixme 等标签。

  • 提供维护和修改指导思路。承认代码瑕疵与常量注释。

  • 站在阅读者的角度写注释。主要讲了可以利用注释提供高级别的抽象解释,也就是代码背后的意图。

  • 同时写注释,是要不怕麻烦,反复提炼才行。

发布于: 2020 年 06 月 30 日阅读数: 59
用户头像

多选参数

关注

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

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

评论 (1 条评论)

发布
用户头像
添加封面图就更好了~~
2020 年 06 月 30 日 16:40
回复
没有更多了
可读代码编写炸鸡四(上篇) - 来写注释