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

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

第七章 错误处理

错误处理简单来说就是当软件出现错误时还能正常工作。错误处理很重要,但不能打乱的原本的代码逻辑。

使用异常处理而非返回码,底层往上抛,最上层集中处理。这点在第三章函数中也提到过,之所以看到有的地方是使用错误返回码,是因为早期的一些语言没有异常处理机制。现在的语言基本都有异常处理机制。

异常的信息应该足够充分(包含出错的位置以及原因)。

不要在catch块中去实现业务逻辑,就是说当出现异常的时候一定要抛出,而不要改变状态或是做其他一些操作,这样会留下很多陷进。

底层的方法不要返回Null值,否则在调用时会添加很多的判断,可以抛出异常或返回特例对象,特例对象是指返回一个函数返回值类型的空对象。

不要传递Null值。

第八章 边界

第九章 单元测试

我工作以来所经历的公司中都很少使用单元测试,以致于我现在对单元测试这方面还不是特别熟悉,只是在自己的个人项目中写过一些单元测试的代码。在DotNet平台下可以使用VS自带的单元测试功能或是NUnit。

现在有一种编程的方法叫TDD(测试驱动开发),意思是先写单元测试,然后写对应的代码,通过修改调试让写的代码通过单元测试。使用TDD,会使测试覆盖所有的代码,测试代码和生产代码的比例有可能会达到1:1 ,所以也会带来成本的问题。TDD三定律:

  • 在编写不能通过的单元测试前,不可以编写生产代码;
  • 只有编写刚好无法通过的单元测试,不能编译也算不通过;
  • 只可编写刚好足以通过当前失败测试的生产代码。

测试代码和生产代码一样的重要,也需要保持整洁。测试代码要随着生产代码的修改而修改,否则只会产生大量无用的测试代码,而且也会给生产代码的修改带来风险。

单元测试的好处:

  • 有了测试不用担心对代码的修改;
  • 有了测试可以毫无顾虑的去改进架构和设计;

整洁测试的三要素 :可读性、可读性、可读性,测试中的可读性甚至比生成代码中的可读性还要重要。要做到高可读性,测试代码需要满足:明确、简洁和足够的表达力。

每个测试都可以拆分为三个环节:构造测试数据、操作测试数据、检验操作是否达到预期结果。

双重标准:指的是在测试环境中和生产环境中有些条件不必完全一致。生产环境中有时要考虑内存、cpu等性能问题,而在测试环境中不必做这些限制。

每个测试一个断言,不必完全纠结,但单个测试断言数应该最小化。

每个测试函数只测试一个概念,还是单一职责的问题。

整洁的测试代码要满足“FIRST”原则

  • 快速(Fast):测试运行应该够快,如果运行很慢就不会想频繁去运行它,就会导致不能及时发现问题。
  • 独立(Independent):测试应相互独立,某个测试不应成为下个测试的设定条件,应该可以单独运行每个测试。测试如果相互依赖,会导致一连串的测试失败,查找问题比较困难。
  • 可重复(Repeatable):可以在任何环境中重复通过。
  • 自足验证(Self-Validating):测试应该有布尔值输出,无论是成功还是失败。
  • 及时(Timely)测试应及时编写,单元测试应该在使其通过的生产代码编写之前编写。按这种要求及时TDD开发了,但在实际工作中并不是总能遇到TDD开发的团队。

第十章 类

类通常由变量、属性和方法组成。按照书中所讲的Java的约定,类应该由一组变量开始,如果有静态公共常量,应该放在前面,然后是私有静态变量和私有实体变量。公共函数跟在变量之后,一些供公共函数调用的私有工具函数在公共函数之后。

和函数一样,类也应该要尽可能的短小。但和函数不同不是以代码行数来权衡,而是以职责。如果无法准确的为某个类命名,则有可能是该类的职责过多。

单一职责原则(SRP):类或模块应该有且只有一条加以修改的理由。

在实际的工作中很多开发人员往往不会思考这么多,他们只想着让代码可以工作就可以了,所以经常出现几千行的大类。系统应该是有许多短小的类而不是少量巨大的类组成。每个小类有单一的职责,只有一个修改的原因,所有这些小类在一起协同工作完成系统的功能。

高内聚:如果一个类中每个变量都被每个方法所使用,则该类有最大的内聚性。

保持内聚性得到许多短小的类。

上面说到每个类应该有单一职责,就是说类之间应该低耦合,但类的内部应该是高内聚的。如果一个类中的每个变量被每个类使用,则该类具有最大的内聚性。内聚性高说明变量和方法相互关联形成一个逻辑整体。

重构函数也会促使类职责的分离,看下面场景:

将一个有许多变量的大函数拆分成小函数,如果拆出来的代码使用了其中4个变量,那么就会将这4个变量作为参数传入到小函数中,如果使用的变量越多,就意味着小函数的参数越多,这时可以讲这4个变量升级成实体变量,小函数就不需要参数了,可以直接使用这些变量。这样就使内聚性变低,因为类中有很多的变量只为少数的函数服务,这时就可以将这些变量和函数拆分出来,单独成类。

开放闭合原则(OCP)

依赖倒置原则(DIP)

第十一章 系统

这一章主要讲的是怎样在较高的层级–系统层级来保持整洁。

将系统构造和使用分开:软件系统应该将启始过程和启始过程之后的运行时逻辑分离开,在启动过程中构建应用对象,也会存在相互缠结的依赖关系。

本章提到的一些技术概念,下面提供一些参考学习链接:

工厂模式

http://blog.fwhyy.com/2009/11/design-patterns-notes-3-abstract-factory-pattern/

依赖注入(DI)

控制反转(IOC)

http://www.cnblogs.com/leoo2sk/archive/2009/06/17/1504693.html

扩容:不可能一开始就把系统做对,实现好当前客户的需求,然后重构,扩容来实现新的客户需求。

软件系统与物理系统可以类比。他们的架构都可以递增式增长,只要我们持续将关注面恰当的切分。

AOP

http://www.cnblogs.com/zhugenqiang/archive/2008/07/27/1252761.html

第十二章 迭进

Kent Beck关于简单设计的四条规则

1 运行所有测试

  • 不可测试的系统是不可验证的,不可验证就不应该部署;
  • 只要系统是可测试的,就会导向保持类的短小和单一的职责;
  • 紧耦合的代码是难以编写测试的,所以编写测试越多,就会导致使用依赖注入、面向接口编程等来使代码解耦。

2 不可重复

3 表达程序员的意图

  • 选好的变量名、函数名和类名;
  • 要记住,下一个阅读你代码的很可能就是你自己;
  • 添加适当的注释。

4 尽可能减少类和方法的数量

重构

  • 上面的2-4点都可以通过重构的方法来达到;
  • 重构的目的:提升内聚、降低耦合、切分关注面(AOP)、模块化系统性关注面、缩小函数和类的尺寸、选取好的名称等;
  • 好的函数、好的类、好的系统是重构出来的,没有人能一开始就把事情做对;
  • 重构应该要及时,发现了坏味道要及时重构,当然这个需要测试来做保障。