第五章 该写什么样的注释
注释是代码的辅助和补充。很多人都觉得,注释就是解释代码做了什么。没错,但是注释能做的,可不仅仅是这么点。
我们写代码时,脑海中会有很多有价值的信息,这些都对理解这段代码有很大的帮助。但是当编码完成时,展现在阅读者面前的,很多时候只剩下代码了,当时在编码者脑海中的那些有用信息全都不见了。阅读者只通过阅读代码的话,很可能理解的不够全面,即使这个阅读者就是几个月以后的编码者。
写注释的目的就是尽量帮助读者了解得和作者一样多。
1、什么不需要注释
注释是否是越多越好呢?随着编程经验的增加,我们会慢慢发现,注释并非越多越好。这是因为注释会占用阅读真是代码的时间,同时大量的注释会占用大量的屏幕空间。过于啰嗦的注释会打断阅读者的走读逻辑。比如下面的这段代码:
//the class definition for Account
class Account
{
public:
//Constructor
Account();
//Set the profit member to a new value
void SetProfit(double profit);
//return the profit from this account
double GetPorfit();
}; 这段代码的注释就完全没有必要的,函数和类的名字已经能很好地自注释了,再额外加上这些注释就有点画蛇添足了,除非你使用了注释自动生成功能。
需要谨记一点,不要为那些从代码本身就能快速推断出来的事实写注释,这样的注释不会提供任何新的有用信息。不要为了注释而注释。
2、不要给不好的名字加注释--应该把名字改好
有许多函数的名字并不是那么完美地表达了它的实现意图。有时候为了对函数进行更彻底的说明,我们往往给这个函数编写详细的注释。但这是不合适的。因为名字是最直观的展示。阅读代码时,有时候我们不会看到这个函数的具体实现,只会看到对它的调用。如果名字引起误解,会带来很多不必要的烦恼。函数的真实意图,需要阅读者跳到函数的实现代码前去阅读注释。这不算是可读性良好的代码。这种情况下,首先需要作出修改的,是函数的名字,而不是添加注释。
一个好的名字比一段好的注释要好,因为在任何用到这个函数的地方你都能看到它。
比如下面这个例子:
//releases the handle for this key. this doesn't modify the actual registry.
void DeleteRegistry(RegistryKey* key); 如果我们在阅读代码时,看到对这个函数的调用,是不是第一印象会认为它是要删除注册表呢?但是如果回头去看这个函数的注释,会发现编码者已经发现了这个困惑,并通过注释澄清了它。但是如果使用下面的这个名字,就不会带来这样的问题了。
void ReleaseRegistryHandle(RegistryKey* key); 而且,我们也不用给它注释了,因为它的名字就已经说得非常清楚了。
记住:好代码 > 坏代码 + 好注释。
3、记录你的思想
前边讨论了什么情况下不需要注释,下面该讨论下哪些情形下需要添加注释。
如果你看过《纸牌屋》的话,肯定会对主角弗兰克印象深刻。弗兰克经常在剧中,跳出角色限制,以独白的方式表达自己的真实看法。这个方法,在以前陈小春版的《鹿鼎记》中也使用过。
我们编码时也需要这样。如果我们在编码过程中,对这段代码有自己独到的见解,就可以采用这种方式,通过注释的方式记录下来,为其他人提供参考。比如像这样:
//这个类正在变得越来越乱
//也许我们应该建立一个子类来帮助整理 通过这段注释,我们可以直观的看出这个类的问题所在,而且注释中已经提供了方法来鼓励后来的编码者对这段代码做出优化。
还有一种情况,代码中始终会存在这样或那样的瑕疵。编码者需要做的是面对这些瑕疵,而不是逃避。比如:
//TODO:采用更快的算法 注释这些,最重要的一个方面是我们可以随时把自己的想法记录下来。这些想法会随着代码传导给阅读者或是下一个编码者,有可能为以后的编码工作提供了方向。
4、给常量加注释
大多数情况下,通过宏定义的常量能很好地阐述自身,不过某些情况下依然会存在困惑。比如:
const int IMAGE_QUALITY = 0.72; //users thought 0.72 gave the best size/quality tradeoff 虽然我们明白0.72的意思,但是如果没有这段注释,就有可能会困惑,设定0.72这个值的依据是什么,是否可以修改。可见,这段注释,并不是画蛇添足。
5、站在读者的角度
编写可读性的代码有一个通用的技术,那就是想象你的代码对于外人来讲看起来像什么样子。他对你的代码一无所知,试着去查找代码中对他来说,可能会带来困惑的地方,这些地方就是需要添加注释的地方。如果代码中有某些地方是使用时需要特别注意的,最好用注释的形式显示地表达出来。比如:
//调用外部服务来发送邮件。(一分钟后超时)
void SendEmail(string to, string subject, string body); 这个函数需要调用外部服务时能够被快速的响应,因为它一分钟的超时限制。如果调用者不知道这个限制,可能会造成他始终调用这个函数出错,却不知道是因为外部服务没有快速响应而导致超时了。预期让调用者慢慢地查找问题来发现这点,不如提前声明。
6、全局观的注释
在文件或是类的开头,我们最好写上对这个文件或是类的概括性描述,姑且叫它为全局性注释吧。
这种注释往往能很好地展示各个函数之间如何工作,各个类如何使用,整个文件的入口在哪,如何结束。它需要很好地概括这个文件或是类的功能,对于不关心的调用者来说,帮助他们屏蔽了这些功能的具体实现细节,只关注于这些代码的执行结果即可。
这类注释最好能说明“做什么”、“为什么”、“怎么做”这三个问题。
7、克服“作者心理阻滞”
编码者往往不希望写注释,毕竟写注释要花费一些工夫。尤其是在修改了代码之后,注释往往没有随之更新。克服这种不好的习惯,我们需要在编码时就动手开始写,而不是写完之后,再去弥补注释。注释也是不要不断改进的。比如:
//哦,天啊,如果一旦这东西在列表中有重复的话会变得很难处理的。
//小心:这段代码不会处理列表中的重复(因为这很难做到) 以上两句注释,第二句就比第一句专业一点,更简练。这也需要我们不断地练习。
|