破窗理论

破窗理论,原义指窗户破损了,建筑无人照管,人们放任窗户继续破损,最终自己也参与破坏活动,在外墙上涂鸦,任垃圾堆积,最后走向倾颓。破窗理论在实际中非常容易出现,往往第一个人的代码写的不好,第二个人就会有类似“反正他已经写成这样了,那我也只能这样了”的思想,导致代码越维护越冗杂,最后一刻轰然坍塌,变成无人想去维护的垃圾。

整洁的代码

整洁的代码如同优美的散文,试想读过的一本好书,能够随着作者的笔锋跌宕起伏,充满了画面感,调动了自己的喜怒哀乐。代码虽然没有那样的高潮迭起,但整洁的代码应当充满张力,能够在某一时刻利用这种张力将情节推向高潮。写代码类比于写文章讲故事,写代码是创作的过程,作者需要将自己想表达的东西通过代码的形式展现出来,而整洁的代码如同讲故事一般,娓娓道来,引人入胜,不好的代码则让人感觉毫无头绪,通篇不知道在讲什么。

整洁代码原则

在现代化的前端开发中,有很多自动化工具可以帮助我们写出规范的代码,如eslint,tslint等各种辅助校验工具,知名的规范如google规范、airbnb规范等等也从各个细节方面约束,帮助我们形成合理规范的代码风格。根据实际重构项目,总结出一系列开发过程中需要时刻注意的原则,按照重要程度优先级排列。

1. DRY(Don’t Repeat Yourself)

相信大家都听说过最基本的DRY原则,很多设计模式,包括面向对象本身,都是在这条原则上做努力。DRY顾名思义,是指“不要重复自己”,它实际上强调了一个抽象性原则,如果同样或类似的代码片段出现了两次以上,那么应该将它抽象成一个通用方法或文件,在需要使用的地方去依赖引入,确保在改动的时候,只需调整一处,所有的地方都改变过来,而不是到每个地方去找到相应的代码来修改。
在实际工作中,我见过两种在这条原则上各自走向极端的代码:

  • 一种是完全没有抽象概念,重复的代码散落在各处,更奇葩的是,有一部分的抽象,但更多的是重复,比如在common下抽取了一个data.js的数据处理文件,部分页面中引用了该文件,而更多页面完全拷贝了该文件中的几个不同方法代码。而作者的意图则是令人啼笑皆非——只用到小部分代码,没必要引入那么整个文件。且不论现代化的前端构建层面可以解决这个问题,即使是引入了整个大文件,这部分多余的代码在gzip之后也不会损失多少性能,但这种到处copy的行为带来后续的维护成本是翻倍的。

  • 对于这种行为还遇到另外一个理由,就是工期时间短,改不动之前的代码,怕造成外网问题,那就拷贝一份相同的逻辑来修改。比如支付逻辑,原有的逻辑为单独的UI浮层+单个支付购买,现在产品提出“打包购买”需求,原有的代码逻辑又比较复杂,出现了“改不动”的现象,于是把UI层和购买逻辑的几个文件整个拷贝过来,修改几下,形成了新的“打包购买”模块,后来产品又提出“按条购买”,按照上述”改不动“原则,又拷贝了一份“按条购买”的模块。这样一来调用处的逻辑就会冗余重复,需要根据不同的购买方式引入不同UI组件和支付逻辑,另外如果新添需求,如支持“分期付款”,那么将改动的是非常多的文件,最可悲的是,最后想要把代码重构为一处统一调用的人,将会面对三份“改不动”的压力,需要众多逻辑中对比分析提取相同之处,工作量已经不能用翻倍来衡量,而这种工作量往往无法得到产品的认同和理解。

  • 另一种极端是过度设计,在写每个逻辑的时候都去抽象,让代码的可读性大大下降,一个简单的for循环都要复用,甚至变量定义,这种代码维护起来也是比较有成本的,还有将迥然不同的逻辑过度抽象,使得抽象方法变得非常复杂,经常“牵一发而动全身”,这种行为也是不可取的。

这也是将该原则排在首位的原因,这种行为导致的重构工作量是最大的,保持良好的代码维护性是一种素养,更是一种责任,如果自己在这方面逃避或偷懒,将把这块工作量翻倍地加在将来别人或自己的身上。

2. SRP(Single Responsibility Principle)

SRP也是一个比较著名的设计原则——单一职责,在面向对象的编程中,认为类应该具有单一职责,一个类的改变动机应当只有一个。
对于前端开发来说,最需要贯彻的思想是函数应当保持单一职责,一个函数应当只做一件事,这样一来是保证函数的可复用性,更单一的函数有更强的复用性,二来可以让整体的代码框架更加清晰,细节都封装在一个个小函数中。另外一点也和单一职责有关,就是无副作用的函数,也称纯函数,我们应当尽量保证纯函数的数量,非纯函数是不可避免的,但应当尽量减少它。
把SRP原则排在第二位,因为它非常的重要,没有人愿意看一团乱麻的逻辑,在维护代码时,如果没有一个清晰的逻辑结构,所有的数据定义、数据处理、DOM操作等等一系列细节的代码全部放在一个函数中,导致这个函数非常的冗长,让人本能地产生心理排斥,不愿去查看内部的逻辑。

3. LKP(Least Knowledge Principle)

LKP原则是最小知识原则,又称“迪米特”法则,也就是说,一个对象应该对另一个对象有最少的了解,你内部如何复杂都没关系,我只关心调用的地方。

4. 可读性基本定理

可读性基本定理——“代码的写法应当使别人理解它所需的时间最小化”。
代码风格和原则不是一概而论的,我们经常需要对一些编码原则和方案进行取舍,例如对于三元表达式的取舍,当我们觉得两种方案都占理时,那么唯一的评判标准就是可读性基本定理,无论写法多么的高超炫技,最好的代码依旧是让人第一时间能够理解的代码。

5. 有意义的命名

代码的可读性绝大部分依赖于变量和函数的命名,一个好的名称能够一针见血地帮助维护者理解逻辑,如同写文章中的“文笔”,文笔优异者总能将故事娓娓道来,引人入胜。
不过要起好名称还是很难的,尤其是我们不是以英语为母语,更是添加了一层障碍,有些人认为纠结在名称上会导致效率变低,开发第一时间应该完成需求的开发。这样说并没有错,我们在开发过程中应当专注于功能逻辑,但不要完全忽视命名,所谓“文笔”是需要锻炼的,思考的越多,命名就会愈加的水到渠成,到后来也就不太会影响工作效率了。
在这里推荐鲍勃大叔提到的童子军规,每一次看自己的代码,都进行一次重构,最简单的重构便是改名,也许一开始觉得命名还比较贴合,但逻辑越写越不符合初始的命名了,当回顾代码时,我们可以顺手对变量和方法进行重新命名,现代编辑工具也很容易做到这一点。

6. 适当的注释维护

注释是一个比较有争议性的话题,有人认为可读的函数变量就很清晰,不需要额外的注释,且注释有不可维护性,适当的注释是有必要的,对于复杂的逻辑,如果有一个简练的注释,对于代码可读性的帮助是极大的,但有些不必要的注释可以去掉,注释的取舍关键在于可读性基本定理

小结

这里提到的6个代码编写的原则,前三个偏向于代码维护性,后三个偏向于代码可读性,整个可维护性和可读性构成了代码的基本素养。作为一名前端开发工程师,想要拥有良好的代码素养,首先要让自己的代码可维护,不给别人的维护带来巨大的成本和工作量,其次尽量保证代码的美观可读,整洁的代码人见人爱,如同阅读一本好书,令人心情愉悦。