序
最近被领导安排,参加了几次codeReview,发现了不少不大不小的问题。因为是另一个小组的,所以我基本上没怎么关注业务逻辑,主要重点放在了对代码风格、抽象层次等的关注,随之发现了一堆问题。
正好我最近在看《代码整洁之道》
这本书,因此我有了整理这方面的一些想法。
本系列将分为上中下三篇,分别从纯理论
、方向
、实践
层面来对代码整洁之道
进行个人的一些见解说明。
方向
上一篇,我们大概介绍了一下关于代码整洁程度的重要性、如何保持代码的整洁等几个宏观的方面,本篇我们将会着重梳理以下几个方面来使你的代码保持整洁。
- 命名
- 函数
- 注释
- 代码格式
- 错误处理
- 边界
- 单元测试
- 迭进设计
- 逐步改进
命名
很多代码复杂度提升的原因,不在于代码的简介程度,而在于代码的模糊程度,即上下文在代码中未被明确体现的程度。
因此我们需要尽可能的使用更能完整表达我们的含义的命名,不要害怕因为想一个名称浪费的那一点点时间,这一点点时间将会为后来的维护带来更多的价值。
关于命名,我们应该尽量做到:
- 提防使用外形相似度较高的名称
- 使用可以读的出来的名称,而不是某种简写
- 使用可以搜索的名称,比如定义常量
函数
在大部分语言中,函数都是占比相当大的一个组成,并且存在Lisp
这种的函数式编程语言,足以见得函数的重要程度,于此同时,函数的规范、简洁程度也应该被人们重视。
在javascript
中,函数是一等公民,因此拥有极高的灵活性,但是也因此更不容易被优美的使用,无用的函数、超长的函数等等层出不穷,为后续的迭代、维护带来莫大的负担。
因此,我们应该重视函数的简洁、规范,让我们的代码变得更为健壮:
- 函数应该尽量短小
- 函数应该遵循
单一职责原则
,也就是函数应该只做一件事,做好这件事,并且只做这一件事 - 不要害怕长名称,长而具有描述性的名称,要比短而令人费解的短名称好,要比描述性的长注释好
- 函数的复杂度往往与参数的个数成正比,因此函数参数应该尽可能的减少,不要多于三个,多余三个应当改为使用对象传参或者考虑是否应该拆分
- 尽量使用
纯函数
,纯函数具有更好的可维护性,更健壮
注释
每个语言中,都存在注释,注释存在的意义就是为了向后来者阐述自己的思路以及想法,因此注释的好坏,往往也会影响一个程序的维护。
注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败。
好的注释会直观的向程序员描述一个程序的意图,然而好的注释可能会随着程序功能的迭代变得不再适用,因此程序员需要将注释保护在可维护、有关联、精确的高度。否则,如果程序员不能坚持维护注释,那么遗留的错误注释将会成为拖累程序可维护性的一个毒瘤。
对于大多数注释,我们可以将其划分为两类注释:好的注释和坏的注释
好的注释(或必不可少的注释)
- 法律信息
- 提供信息的注释
- 对意图的解释
- 帮助某些无法修改的库进行阐释的注释
- 用于警示可能会出现的风险的注释
- TODO
- 用于放大某些看似不合理的代码的重要性的注释
- jsdoc
坏的注释(或没有意义的注释)
- 自说自话,通篇废话,没有主题的描述
- 多余的注释
- 误导性的注释(比如未随着程序一起迭代更新的老旧注释)
- 为了注释而注释(比如声明了一个无关紧要的变量也进行注释)
- 日志式的注释
- 废话注释
- 位置标记
- 归属与署名
- 注释掉的代码
- js注释中使用
html
来进行注释 - 非当前程序的注释
- 信息过多的注释
代码格式
代码格式因为现在已经存在了类似eslint
、prettier
这些风格类工具,因此可以较为简单的说明一下。
代码格式推荐:
- 代码块之间留行分割
- 关系密切的概念应该互相靠近
- 变量声明尽可能靠近其使用位置
- 若某个函数调用了另一个,就应该把他们放到一起,而且调用者应尽可能放在被调用者上面
- 使用短行代码(最多80列或120列)
- 运算符之间应该存在空格
- 应该使用统一的缩进
- 团队应该使用统一的代码风格
错误处理
错误处理是任何一个程序都离不开的话题,从程序自身的语法错误、类型错误,到网络请求的各种响应错误,再到程序间通信的自定义错误,错误处理的方式,影响着程序整体的规整程度以及后续的扩展性。
因此,对于错误处理:
- 使用统一的错误处理模块
- 对错误进行分类处理
- 对各种错误信息应该有着详尽的描述
边界
边界是指一些平时可能不会注意到,但是又会切入程序本身的一些地方,比如我们使用的第三方模块。
对于第三方模块这种边界,应当:
- 编写测试用例来遍览以及理解,确保在版本变化中不会因为第三方模块的破坏式更新造成影响
单元测试
对于一个功能完善的程序来说,单元测试必不可少,他能够提供足够的使用场景供你来对你的程序进行测试,并可以生成测试覆盖率,来帮助你完善各种边缘case。
单元测试一般常用于测试纯函数、纯组件,纯应用保证在同样的使用场景下,使用会保证得到相同的结果。
关于单元测试,我们应该尽量做到:
- 编写好的单元测试,随着功能的修改而及时跟进,需要保持整洁以及有效,防止造成不可维护的测试代码
- 保证单元测试代码的健壮、可维护性,它将会让你的代码可扩展、可维护、可复用
- 保证足够的测试覆盖率
- 在
pre-commit
钩子进行单元测试的执行,通过之后才可进行继续提交 - 单元测试的代码应该足够明确、简洁,并有足够的表达力
- 单元测试的代码应该按照颗粒度进行划分,保证功能单一性,每个测试一个断言
- 要进行单元测试的代码应该保证足够的纯度
单元测试消除了对清理代码就会破坏代码的恐惧
迭进设计
迭进设计是指先实现一个近似的模型,然后收集新的参数,继续实现一个更近似的模型,如此反复。
我们可以通过迭进设计的方式,来进行系统的构建:
- 递增式的完善代码
- 便于修改
- 留有足够的空间对系统进行更优设计
- 可以逐步编写单元测试
逐步改进
要编写整洁代码、必须先写肮脏代码,然后再清理它
我们在进行代码重构的时候,可以使用逐步改进的方法:
- 逐步减少重复代码
- 尽可能减少类和方法的数量
- 可以使用有关优秀软件设计的一切知识,如:
- 提升内聚性
- 降低耦合度
- 切分关注面
- 模块化系统性关注面
- 缩小函数和类的尺寸
- 选用更好的名称等增加表达力
- 编写更为优秀的单元测试
- 采用测试驱动的规则,在保证测试用例始终可以正确运行的前提下,进行渐进式重构
- 重构前整理好要修改的原因,在重构的时候可以进行针对性的优化
小结
本篇从多个方向介绍了如何保证代码的简洁,可以说,这是《代码整洁之道》
一书的精华所在。
下一篇,我们将从一些具体的小例子,来对此进行更详实的描述。
希望看完这三篇,对你有所帮助。