本文讨论的是编码风格、原则和艺术。

来源于公众号:卫sir说 (man-mind)原文章发布于 2022-01-05

至于如何编码实现系统的功能和性能、如何架构设计、如何提高易用性、如何团队协作、如何项目管理,均不在本文讨论范畴之内。

本文也不讲大括号应该放在哪里,代码如何缩进,运算符周围是否加上空格这种规范,虽然这也很重要。

编码风格的所有目标

1、代码易读

2、代码易改

这就足够了。

而且这很难做到。

最重要的技巧

1、小函数;(注意这个技巧排在第一位)

2、小文件、小类;(小的东西,易读,易改)

3、做好单元测试!(所有都测到了,你就敢改)

4、少注释;(而是增加表达力!让人一眼看懂的代码不需要注释)

5、消除重复。(重复永远是丑陋的)

如果你记不住这么多条,就记一条:小函数。

七个习惯

一、保持精简

“小就是美”。

告诉你一个秘诀,即便你不会编码,也可以简单而准确地判断一个人是不是编程高手。

你只需要略微看一下他的代码就可以。

如果他代码中的函数,绝大多数都在20行以内,这就是高手。

如果基本都在10行以内,这是顶尖高手。

如果都在3、4行左右,这是大师。

如果有很多超于100行的函数,这是新手、低手。

类似地,看他代码中每个文件的长度,应该大多都在200行或者400行以内,才是正常的,虽然这无法直接判断是不是高手。

但如果动辄都在2000行以上,这是新手、低手。

大致而言,高手能做到:

1、小函数、小文件、小类;

2、小的单元测试用例。

3、函数有尽量少的参数。

最理想的函数参数数量是0个,其次是1个,再次是2个,应尽量避免3个及以上。

参数越少,越好测试,越不容易搞错参数的顺序。(为减少参数,可考虑封装成类。)

怎么做到这点?只要你有保持精简的意识,你总会做到的。

任何一个文件,一个函数,只要感觉它大到一定程度(比如超过10行),就着手拆分。

下面举个例子:

上图左边是一个长达15行的函数,经过整理后,可以写成右边那样,但这仍然不够。

右边每种颜色的代码块都应该由一个函数完成,所以,左边这个函数,最终变成4行。(每行都是一个函数调用)

类似的,稍微看到一点比较繁复的东西,就抽象它,精简它,让你的程序看上去非常简洁才可以。

这就是在践行KISS原则。(Keep It Simple,Stupid)

也是在践行“松耦合、高内聚”原则。(一个小函数、小类,必然是高度内聚的)

二、提高表达力

编写整洁、易读的代码。

高效能人士写的代码,即便在10年后,他把代码已经忘光光的时候,只要需要,他拿出来一看,就能轻松理解。

我写代码,从来不是为了别人看,我只是怕自己忘记。

我的记性不太好,而且我从来不依赖我的记性,所以,我必须写得非常易读。我很难忍受看不懂自己代码而产生的那种嫌弃感。(不是嫌弃现在的自己,而是以前的自己)

“代码的写法应当使别人理解它所需的时间最小化。”

如何做到?下面谈5点。

1、让代码读起来像自然语言。(不断磨练这点)

2、高度重视变量和函数的命名,要明确、无歧义。

所有函数命名,都以动词为开头。(类名用名词)

不要怕命名长,只要有助于理解,长点没事。

慎重选择用词,考虑不会让人误解。

举个例子:

上面这段代码是拷贝字符串的,如果参数名改为source和destination,就会清晰很多,而且,引用者会不太容易搞错参数顺序。

3、在实在没有办法用代码表达意图的时候,写注释。

除去版权和许可证这种必要的注释,绝大多数情况下,我只在觉得自己以后可能看不懂的情况写注释。

在比较古板的条条框框下,注释应该占代码的1/5以上。

然而,对高手而言:在大多数情况下,注释表明了一种失败。

所以,在你想注释的时候,再努力一下,看能不能仅用代码就可以明白表达。

“不要给不好的名字加注释——应该把名字改好”

注释不是代码,所以它是难以管理和测试的。由于人的懒惰天性,注释通常不会跟着代码的改动而改动,所以注释往往是过时的、错误的(而且难以发现)。

“不准确的注释要比没注释坏得多。它们满口胡言。”

4、照顾阅读者的感受

把关系相近的函数,放到一起。

如果函数 A 调用函数 B,那么把 A 放到 B 前面,人就会自然阅读下去。而且阅读者会有一个预期,看到一个新的函数,知道很快就能读到它的实现。

(不过我的习惯是:把 B 放到 A 前面,因为有些编程语言,要求一个函数只能调用前面出现过的函数,如果不加声明的话。所以,看我的程序,需要从文件底部看起。)

同样地,概念相关的代码,放到一起,用空行分开。

不要再用微软提倡的匈牙利标记法(比如 szAuthor 这种命名),那已经过时了。使用大小写或者下划线分割单词的命名(比如 deletePage)就很好,匈牙利标记法比较丑陋,直接影响阅读感受。

再举一个例子:为了便于阅读,避免使用 do / while 语句。

do / while 这种写法很丑陋,通常来讲,逻辑条件应该出现在它们所“保护”的代码之前,if / while 和 for 语句就是这么干的。而 do / while 把条件放在后面,这很反常,以至于大多数人读到 do / while 时,都需要读两遍。

5、不要重复!

“重复可能是软件中一切邪恶的根源。

“重复”会浪费编码者的时间、浪费阅读者的时间、浪费维护者的时间、浪费编译、运行的时间。而且,还浪费存储。

看到重复,就改写它,这是一个基本素质。

三、边写边测

程序如果易读,就很有利于修改。

但仅仅“易读”是远远不够的。

很多程序员敢写代码,但不敢改,他们都知道什么是“牵一发而动全身”。

所以,每次你让他们改程序的时候,他们的脸色都不好看的。

只有顶尖高手,才会“欣然改程序”,他们知道,这又是一次展示自己能力的机会。

他们是怎么做到这点的?

他们的底气来自测试。

测试覆盖率越高,你就越放心,你就可以放心地修改,甚至放心地改架构!

下面是TDD(来自极限编程)的方法论,值得参考和借鉴。(说实话,我并没有严格做到)

1.在写功能代码前,先写一个小的单元测试。(不要写太多,否则你会没有动力)

2.运行所有测试(先看这个测试是否能工作),测试应该失败。(因为还没有写生产代码)

3.编写仅仅能通过这个测试的最简单代码。(先不用写很好,后面还会重构;也不要写更多,因为其他还没写测试)

4.通过所有测试。(注意是所有测试,这保证了一切都是对的。)

5.根据需要重构,每次重构后都测试。(始终保持一切都是对的。)

6.重复上述循环。(测试和编码应该是小型和渐进的)

测试用例应该有这样的结构:设置、执行、验证以及可能需要的清理。

测试用例应该尽量小,一次只干一件事。

坚持TDD原则,你就会有覆盖率足够高的测试代码,否则可就不一定。

注意:“测试代码和生产代码一样重要。它可不是二等公民。它需要被思考、被设计和被照料。它该像生产代码一般保持整洁。”

当然,写测试代码本身是需要成本的,但是你要好好想想,值不值?

四、时时清理

所有东西,如果不维护,总是会变脏、变坏。(这就是这两年很多人喜欢说的“熵增”)

代码也一样。(除非你再也不用它了)

所以,每次你改动代码时,都要让代码变得更整洁。

正如美国童子军军规:“让营地比你来时更干净。”

注意,你敢于清理的前提是,你有足够的测试用例。

每次清理时,告诫自己,这会给自己带来巨大好处:可以避免今后巨大的麻烦。

这可以用在工作和生活中的任何方面,包括你的思想。

五、同时保持两种截然相反的思维

很多思维,都有截然相反的对立面,并且都有其拥趸。

比如有人说WEB3是未来,就有人说WEB3是垃圾。

有人说公司要人性化管理,就有人说必须要狼性管理。

再举两、三个例子:

1、应该自顶向下编程,还是自底向上编程?

正确答案是,这两种思维都要用。

2、先写代码,再写测试;还是先写测试,再写代码?

你自己看着办。

3、“三目运算符”的可读性也是有争议的。

拥护者认为这种方式简洁,只有一行;反对者则说这可能会造成阅读的混乱。

是下面这样好,

time_str += (hour >= 12) ? "pm" : "am";

 

还是下面这样好?

if (hour >= 12) {
    time_str += "pm";
} else {
    time_str += "am";
}

 

正确做法:默认情况下都用if/else,只在最简单的情况下使用“三目运算符”。

保持两种思维的好处在于,你可以始终对这两种提法进行实践、测试、反思,在两种极端的说法中找到平衡,找到各自适用的场景,找到更适合自己的做法。

这会让你更从容、更灵活。

甚至,不要一味追求“小就是美”,偶尔大一下也没啥。

六、知道不做什么

最好读的代码就是没有代码。

如果有现成的,不要自己去做。

如果现在还用不到,就先不要写。(YAGNI原则:You Ain’t Gonna Need It)

你所写的每一行代码都是要测试和维护的。

你要节省自己的时间,你还有更重要的事要做。

七、更新自我

自FORTRAN语言在上世纪50年代后期出现之后,在编程思想方面,人们就在不断的学习和长进。

人们首先认识到封装的重要性,ALGOL语言的设计者在ALGOL60中采用了以Begin……End为标识的程序块,使块内变量名是局部的,以避免它们与程序中块外的同名变量相冲突,这是编程语言中首次提供封装的尝试。

上世纪60年代后期,随着《程序结构理论》和《GOTO语句有害论》的提出,证明了任何程序的逻辑结构都可以用顺序结构、选择结构和循环结构来表示,确立了结构化程序设计思想,这使得程序更容易维护。

上世纪80年代,面向对象程序设计思想经过20年的研究和发展逐渐成熟,一大批面向对象语言相继出现。

2001年2月,在美国犹他州瓦萨奇山,17个来自于各类敏捷方法的实践者共同达成了一个共识:《敏捷宣言》,提出了十二条敏捷原则。

其中第十条原则和本文密切相关:

简洁是敏捷的精髓,它是极力减少不必要工作的艺术。

Simplicity–the art of maximizing the amount of work not done–is essential.

然后,设计模式、重构开始普遍流行。

再然后,有了CI/CD,有了Devops。

以前的程序员,是不懂这些的。

如果你是搞IT的,你就会发现,编程思想、方法、语言、工具,层出不穷、学无止境。

真正的高效能人士,永远不会停止学习,永远不会停止更新自我。

因为永远会有更好的东西问世。

编程之外

我在命名这7个习惯时,刻意使用了普适的说法。

这些高度抽象的理论,完全可以用在其他领域。

  1. 保持精简
  2. 提高表达力
  3. 边写边测
  4. 时时清理
  5. 同时保持两种截然相反的思维
  6. 知道不做什么
  7. 更新自我

这些一样可以用在写作、工作、生活以及做人上。

成熟的人,都明白这点。

图|高效能编程人士的七个习惯


本文用标题和图示向Stephen R. Covey致敬,感谢他的神书《高效能人士的七个习惯》。

本文引用主要来自以下两本广为人知的名著:

Robert C. Martin. 代码整洁之道. 人民邮电出版社.

Dustin Boswell,Trevor Foucher. 编写可读代码的艺术. 机械工业出版社.

文|卫剑钒


本文被阅读了:1,606次

作者头像
关于  Ailitonia

正因站在了巨人的肩膀上,才越发觉得自己渺小。不求成为巨人,但求与其同行。 把自己所见所闻,记录下来。

→查看所有由Ailitonia发布的文章