《代码整洁之道》读书笔记(上)

全书一共400多页,一共17章,第十三章讲并发,并且在附录A中有对并发的补充,第十四到十六章是一些Java代码的案例,第十七章相当于一个总结。本次写读书笔记主要涵盖前十二章的内容,由于篇幅分为上下两篇。

第一章 整洁代码

代码的质量非常重要,糟糕的代码有可能会毁了一个公司。对于一个很注重代码质量的人来说呆在一个只关注交付而不关注代码质量的公司是很痛苦的。

什么是整洁的代码,不同的人又不同的定义。我认为整洁的代码应该是符合所使用语言代码规范的;可复用的;便于维护的;简洁的。

糟糕的代码想做太多的事情,意图混乱;整洁的代码的每个函数、类和模块都全神贯注一件事,不受周围的干扰。

“破窗理论”:指的是窗户破损了的建筑让人觉得似乎无人照管,于是别人也不关心,任由窗户继续破损,最终自己也参与破坏活动,在墙上涂鸦,任垃圾堆积,一扇破损的窗户让该建筑走向了倾颓的道路。在程序中也是一样,一处出现了有坏味道的代码,有可能导致整个项目的代码都满目疮痍。所以到嗅到代码有坏味道的时候就要重构,我认为整洁的代码是重构出来的。

美国“童子军军规”中有条说道“让一地比你来时更干净”,映射到我们编码中就是我们每日在提交代码的时候要确保代码时整洁的,否则就进行重构。好的代码是靠大家一点点积累出来的。问题发现的越早,修改的成本就越少。

第二章 有意义的命名

本章主要介绍在程序中命名的规范,命名随处可见,变量、函数、类、模块、命名空间等都需要有好的命名。一个名称如果能让阅读者一看就知道要表达的意思,就可以认为是一个好的名称。

不要使用单个字母来做变量名,时间一长,自己都不清楚自己当初的命名是什么意思。小方法体,如循环中的计数器除外。

不要使用有误导性的字母作为变量名,比如小写字母l和大写字母O,因为他们和数字的一和零很像,有的字体还比较好区分,但大多数字体很难分辨。

“匈牙利命名法(HN)”:该命名法是一位叫 Charles Simonyi 的匈牙利程序员发明的,后来他在微软呆了几年,于是这种命名法就通过微软的各种产品和文档资料向世界传播开了。在当时那个时代编译器并不做类型检查,程序员需要使用HN来帮助自己记住类型。现在的一些高级静态编程语言具有更丰富的类型系统,编译器可以很好的做类型检查,所以使用NH纯属多余。使用NH的几个弊端:

  • 增加了名称的长度;
  • 使名称变得不可读,而在本章的2.5小节有强调要使用可以读得出来的名称;
  • 增加了修改名称的难度,修改了变量的类型,变量名就要随着修改,否则会造成误导。

本章中讲到对接口的命名不要使用“I”作为前缀,这点我持保留意见,可能因为我一直是从事的Net上的开发,DotNet的类库中的接口基本都是使用“I”作为前缀的,而且在《NET 设计规范》一书中也强调接口要使用“I”作为前缀。

类名使用名词或是名词短语,而不应当使用动词。

对于方法名做到每个概念一个词,应该保持一致,比如对于绑定数据的方法,不要有的地方用BindData,而另一些地方使用DataBind 。

最后想说的是命名除了一些通用的法则外,对于一些规范性的问题还是要遵循所使用语言或平台规定或是约定俗成的惯例。比如方法名C#中推荐使用Passcal风格,而在Java中则是使用Camel风格。

第三章 函数

函数一直都被要求要短小,有不少书中都以行数来作为标准,比如一个行数在20行以内被称为小函数,或是要在5行以内才是小函数。以行数来要求似乎有些苛刻,有一些极端的情况,比如初始化一个Model,里面有几十个字段,这时这个初始化函数中就有几十行代码,而且是无法拆分的,所以我认为,函数只要是在做一件事情就可以了。通常来说如果函数只做一件事,自然就不会很长。我对函数长度的极限是横竖都不要超过一屏。

函数应该只做一件事,判断函数时候只做一件事,看函数中是否很能够拆分,如果可以,就果断进行重构。

当函数中有Swicth语句的时候,就不可避免的要做多件事情了,而且函数会随着Switch条件的增加会越来越长。因为函数中做了多件事情,违反了SRP原则,因为随着增加条件我们要去修改这个函数,违反了OCP原则。这个问题可以通过工厂模式来解决。

函数的名称要使用描述性的名称,让人一看名称就知道该函数是做什么的。当函数只做一件事情的时候,取名就容易多了。还有比较重要的一点,风格要保持一致。

函数的参数要尽可能的少,书中要求是最好不要超过3个参数,当超过的时候就要将参数提取为一个对象,但实际中很难做到,通常都是为了省事,但有N多参数的函数会给阅读者带来障碍,特别是当参数名还词不达意的时候。

在一个函数中不要去调用职责之外的另外的函数,尤其是底层的函数,否则给高层调用带来风险。举个简单的例子:比如在用户登录的时候我们可能会有一个CheckPassword的方法来验证登录的用户名和密码,如果在CheckPassword函数中在验证成功后调用Session.Init()来对Session进行初始化,就会存在隐患。根据名称来看只是检查密码用,如果有人在非登录的情况下调用了该方法,会更改当前会话。

使用异常代替错误返回码,如果使用错误返回码会要求立即处理错误,当在高层调用很多底层方法时,每个方法都要去根据错误返回码进行处理,会造成函数逻辑混乱,如果使用异常处理则只需要在catch中处理即可。

远离重复,拒绝重复,方法有很多,抽象到基类或放到底层公共类库中。

没有人能一次性就将函数写的很完美,好的函数是通过重构得到的。

第四章 注释

注释是一把双刃剑,好的注释能够给我们好的指导,不好的注释只会将我们误导。注释是弥补代码子表述不足的一种手段,就像设计模式是用来弥补语言不足一样。

代码是我们获取信息的准确来源,注释随着项目人员的更替 反复的修改最终可能词不达意了,因为很多开发人员在整合代码,修改方法的时候并不是总是同步修改注释。

有时候看到一个函数的代码写的很糟糕,逻辑很混乱,有开发人员可能想,给这个函数加上几行注释,这样有可能起到适得其反的作用,这时要做的是将函数整理干净。

代码即注释,很多书和大师都这么讲,意思是我们要用代码本身来解释我们的意图,那就要求我们要控制好函数只做一件事,函数名和变量名要规范和可读。

当然也不是所有的注释都没有用,像下面几种类型的注释是有必要的:

  • 具有警示性的注释;
  • 描述一些负责业务场景;
  • 有些函数现在还是一个空壳,但在将来可能有用,有必要写。

当我们不得不写一些注释的时候,要确保言简意赅,能够很好的表达意思,不要造成误解,也不要写多余的废话。

还有一种日志被称为日志式注释,一般出现在一个类的开始部分,记录每次修改时的时间、人员名称和修改内容。随着时间的推移这类注释会变得非常冗长。这类注释在一些项目中很普遍,而且有时会被严格要求写,但书中强调现在的源代码都会有源代码工具来进行管理,修改记录在源代码工具中有保存,这种日志式的注释应该全部删除。

有的开发人员喜欢在注释中签上自己的名字,这种做法也没没有必要,因为我们有源码管理工具。

项目代码中经常会出现被注释掉的代码,这对后面的维护人员会造成困扰,也会使代码变得混乱,这种代码同样可以删掉,因为我们有源码管理工具。

第五章 格式

代码并非可以工作就了事了,还要有良好的代码格式规范,开发人员在维护代码的时候经常会埋怨别人的代码写的很烂,如果不严格要求自己,以后就会成为别人眼中的“别人”了。

代码格式规范,我认为首先要保证和当前使用的语言、平台保持一致,再就是整个团队中要保持一致。当公司项目的代码规范和使用语言、平台的规范有冲突时尽量说服团队和语言、平台保持一致,实在不行只能和项目组保持一致,像我现在所做的项目由于一些原因还在使用匈牙利命名法,虽然这让我很郁闷,但我还是和他们保持一致。如果是一个新项目则可以团队成员在一起来讨论制定代码规范。

合理使用空白行,同一类的有关联的放在一起。

变量申明尽可能靠近使用的地方。

实体变量放在类的顶部,因为设计良好的类,实体变量会被大多数的方法使用。

相关函数(一个函数调用了另一个函数)放在一起,也包括的重载的函数。

一行代码的宽度:120字符之内,最好不要拖动横向滚动条。

横向空格,比如一个表达式中的符号左边和右边与符号之间加空格,这个在VS中的代码格式化会自动帮我们做了。

缩进,只有一行的也按缩进规则来。

第六章 对象和数据结构

暂略