注释
作者在书中说到,没有注释才是最好的注释。当我们要使用注释的时候,这说明我们的代码并不能很直观的表达我们想要表达的逻辑,注释是给人看的,目的是为了让读代码的人能更好的理解程序的意思。如果我们代码已经能够很清晰地表达这些逻辑,不会让人产生误解,那么注释就是多余的。因为并不是信息越多就是越好,信息越多,越会分散人的注意力,让人不能把注意力集中在真正重要的地方,同时注释不能像命名那样跟着变量走。当代码发生变动,我们通常会忘记去处理对应的注释,这样时间一长,会导致错误且具有误导性的注释。但是,有些时候,比如面对晦涩难懂的算法,我们很难用程序直观的表示,我们还是得借助注释。这里列出一些作者认为比较重要的东西,你可以参考一下:
-
在写注释前,须谨慎,思考能不能通过改变代码来避免去写注释
就像我们上次讲的一样,有些时候,我们可以将晦涩难懂的模块进行拆解、封装,通过简短的函数或者是模块名来让逻辑变的更加清晰,用程序本身来解释程序是最好的。每次写注释之前,都可以思考一下优化代码这个途径是否能走通,不能走通的话再考虑写注释,千万不要把写注释当成是弥补你程序没有写好的一个工具,长期看,这只会让程序变得更糟。
-
写下的注释一定要能很好的记录和查阅
有些部分的代码,我们会觉得当下条件不允许,需要以后来写。这个时候,我们往往会在对应的位置加上
TODO,这么做是没有错,但是之后一定要记得回过来查阅。有些 IDE 有全局查看TODO的功能,可以借助。及时将没做的部分补上,将对应的注释删掉。总之,写下的注释或许会是你欠下的债,要牢记在心。 -
写注释的几大误区
- 任何注释都不应该让读者去到其他的模块找答案。因为这会无形间增添模块间的耦合度,同时,意味着信息量的增加,不易于读者理解代码。
- 不需要的部分直接删除,不要注释。常常我们会有这样的习惯,写了一长串的代码后,发现逻辑或者数据方面有些不太对,但是自己辛辛苦苦花时间写的代码又不忍心删掉,于是就注释掉,作为新思路的参考。但是当你 push 代码到 remote 的时候,一定要把这些注释部分删掉。因为别人看到这些代码会潜意识里认为这是重要的代码不能删,然后这些代码就一直累积下来,你不删就没人敢删,这些代码在这边没有任何的价值,反而无形中增添了很多干扰。
- 当注释还需要其他注释解释的时候,那么这个注释就是失败的。注释本身的目的就是解释程序的,而不是提出问题的。
- 不要给当前解释的程序增添系统的逻辑。在写注释时,我们需要限定注释的解释范围,限定在当前的模块,甚至是当前的函数是最好的,还是那句话,范围越大耦合度越高,等到注释中说的那个部分改变了,你会很难想到要回头改注释。
- 需要区分什么是 Git 干的事,什么才是注释干的事。有一些新手程序员,为了表示自己认真,每个文件都会写上作者,修改时间,版本之类的信息,还会在注释中提及版本和改动。这些事情其实都是版本控制工具去做的事情,我们并不需要依赖注释,这只会让程序混乱。
总之,注释有时可以帮助我们阐述逻辑,但有些时候会产生误导,写注释之前要三思。
代码风格
代码风格就是代码展现在我们眼前的形式,往往会影响我们阅读代码的效率。我们必须从人的视觉习惯出发,发现正确的代码风格,然后养成书写这样风格的习惯。
书中主要从两方面来阐述代码风格,一个是从垂直方向,另一个是水平方向。不管如何,我们还是列一下这里的重点部分:
-
从上到下,从总体到细节
可以参考新闻报纸,一张报纸上面你可以找到不同的主题。从标题,到副标题,再到段首,再到每段的细节句子。我们可以看到,我们读报纸是从整体概览到细节这样一个抽丝剥茧的过程。读代码其实和读文章是一样的,比如一个函数,我们先看到的是函数名,这个是对这个函数功能的一个概览,看到它,其实可以决定你需不需要继续往下看这个函数,细节部分会随着我们逐渐往下看而增多。因此代码风格上也要体现这一点。
-
用垂直间距来表示每行的关系
函数之间我们习惯用一行空格隔开,表示这是两个不同的模块。在一个函数中,变量定义的时候,我们也可以将相互关联的变量定义在一起,没有直接关联的变量定义用空格隔开。有关联,比如是相互依赖紧密的函数的定义可以放在靠近的位置。个人感觉这是一个习惯的问题,当你习惯这样了,你阅读你之前写的代码会更容易发现思路与逻辑。
-
水平距离同样重要
上面说了垂直距离可以表示两个变量或者是函数的关联紧密程度。水平距离也是如此,就比如在 JAVA 中,我们习惯函数名与其后面的括号没有空格,这是因为函数的输入参数是函数的一部分,不应该相隔开来,但是输入变量之间是相互独立的,所以输入变量我们习惯用
,+ 空格 进行分隔。在一些数学计算式子中,我们同样可以利用这一点,比如:b*b + a*c相对于加法,乘法具有优先计算的性质,这中间的空格就很能表示这一点。
对于风格这一点,并没有特别严格的要求,只是说能够遵守大部分人的视觉习惯就好了。但重要的是,你选择了一种风格,或者是一个团队选择了一种风格,就需要贯彻地坚持执行下去,我们希望整个代码库中的代码风格都是同一种风格,这样阅读起来才会更高效,看起来也会更加一致。
对象与数据结构
这一章主要就是探讨了一个问题,“对象和数据结构到底是不是同一个东西?如果不是的话,怎么区分?” 之前我们一想到数据结构都会和算法联系在一起,这里我们把它和对象放在一起,主要是想探讨它们在代码结构层面的区别。
如果在内存层面,甚至说是算法层面,对象其实也是数据结构。这两个的主要区别在于抽象和封装这个层面。数据结构里面的东西都是对外开放的,它只是数据传递过程中的一个工具,数据结构对其内部存放的数据并没有权限管理。但是对象不同,通常意义上讲,对象中的数据一般都是私密的,也就是 private 的,对象一般对外只提供对应的功能函数。这么做的目的是希望隐藏自己不必要的内部逻辑,只展现外界需要知道的接口函数,对象在这里就像是一个黑盒子,你不需要知道其内部的具体实现,你只需要对其进行输入,它就会给你对应的输出。这说到底其实是 面向对象 这种编程模式的思想。另外还有一种古老的编程模式,那就是以 C 语言为首的 过程式编程,数据结构的思想,也就是数据没有封装,没有权限的思想在这里得到了展现。说到这里,你可能会认为过程式编程早已经被淘汰,现在一切都是对象,我们要拥抱面向对象编程,别急,先看看书中的一句原话:
Procedural code makes it hard to add new data structures because all the functions must change. OO code makes it hard to add new function because all the classes must change.
如果你对面向对象有一定了解的话,你应该不会对上面这句话感到陌生。JAVA 中如果一个类要 implement 一个接口,那么这个接口中的所有函数都需要被实现,继承也是同样的道理。这就会导致函数的耦合性升高,导致在类中,或者是接口中增减函数变得不那么自然。但是过程式编程就不存在这个问题。所以技术往往并不绝对,需要根据具体的场景灵活选择。但是切记的是,对于一个地方,你只能使用一种模式,要不就是面向对象,要不就是过程式,二者放在一起的话容易产生混淆,且容易出错。
总结
今天的话题就说到这里,学到的一点是,很多东西需要养成良好的习惯,下次遇到的时候就不会犹豫不决。