Clean Code 摘录
一直以为,Java
是一门非常依赖IDE
的语言。这本书里,Bob 大叔
其实也不断的提及现代工具、IDE
等词。
第二章 有意义的命名
别用accountList 来指称一组账号,除非它真的是List类型。用accountGroup 或 bunchOfAccounts, 甚至直接用accounts都会好一些。 即便容器是个List,最好也别再名称中写出容器类型名
note: 对Python
真的适用这个规则吗?
并不非常同意2.7.2 成员前缀
的观点,在c
中,我看到的开源项目cPython
Linux
都大量的在命名中加入类型前缀或后缀,
对于自己在Python
开发时,因为Python
是动态语言,因此自己也会在命名前加入类型前缀,如l_, d_, s_,
来表示常见的类型
list, dict, set
, 当然s_
可能被理解为str
。
读后感
- 命名当然是很关键,尤其是和别人讨论时。
- 在代码方面,书本提供的代码,其实很大一部分可以归结为: 避免在代码中直接使用常量,试着给每个常量一个有意义的名称, 这样不管对于阅读代码时搜索变量,还是阅读时对代码的理解都是极好的。
第三章 函数
函数参数尽可能的少,从编写测试用例的角度看,确实如此。每个参数都有正常情况和多个异常情况,多个参数的函数,就需要排列组合个 测试用例来完全覆盖这个函数,确实令人头大。
标识参数丑陋不堪。向函数传入布尔值简直就是骇人听闻的做法。这样做,方法签名立刻变得复杂起来,大声宣布本函数不止做一件事。
简单的重构为 if true: do_something() else: do_other()
更加直接。
函数应该只做一件事。错误处理就是一件事。
3.12 如何写出这样的函数
写代码和写别的东西很像。在写论文或文章时,你先想什么就写什么,然后再打磨它。 初稿也许粗陋无序,你就斟酌推敲,直到到达你心目中的样子。
我写函数时,一开始都冗长而复杂。有太多缩进和嵌套循环。有过长的参数列表。 名称是随意取得,也会有重复的代码。不过我会配上一套单元测试,覆盖每行丑陋的代码。
然后我打磨这些代码,分解函数、修改名称、消除重复。我缩短和重新安置方法。有时我还拆散类。同时保持测试通过。
最后我遵循本章列出的规则,我组装好这些函数。
我并不从一开始就按照规则写函数。我想没人做得到。
读后感
- 函数的参数当然是越少越好,这样也能体现一个函数只做一件事的原则,但是有时候一个复杂的类的初始化确实很难做到。
- 最后一段讲的真的非常棒。没人能在一开始就实现一个完美的、符合规则的函数。先实现一个可以跑的,再不断的分解它、优化它。
第四章 注释
注释不能美化糟糕的代码。很多时候,只需要创建一个描述与注释所言同一个事物的函数即可。
TODO
表示 我知道这里有问题,是可以改进的,但是我懒得改 😄。不过,还是要抽空定期查看todo list
,do
一些。
读了这章,感觉作者有点认为注释是弊大于利的。但是,其实我觉得自己注释写的还是比较多的, 所以总想找个不能用代码来阐明注释的例子来反驳下, 其实工作中还是会经常遇到一些奇葩的需求,但是放到这里有点不合适。 不过最后作者还是举了一个算素数时取平方根作为循环限制的例子,来说明注释的必要性。 除去那些废话式、多余式的注释外,我觉得想要注释能发挥它的作用,就需要像维护代码一样维护注释, 保持代码整洁,也要保持注释整洁, 这其实就要求所有的开发者都有这样的意识。 否则,只要其中一人在修改代码后没有更新注释,可能就会误导后面的人。
尤其在一些代码都是拷贝粘贴、又只改变一点点的时候,被拷贝代码里的注释往往让以后的使用、维护者难受。 这种代码往往是一些脚本代码,属于“食之无味,弃之可惜”的,自己不想写,就直接复制一份差不多的小改一下。
第五章 格式
- 主次分明,在源文件最顶部给出高层次概念和算法。具体细节在下面渐次展开。
- 善用空行,用空行来划分具体函数内的层次,像
C
一般将局部变量定义放在函数最前面,然后空一行写具体逻辑,最后空一行return
。Python
虽然推荐在需要使用变量的时候再定义它,但是一些具体变量,如打开文件,初始化类等都可以放在函数的开头,一些临时的变量, 如for
循环的index
计数变量,就放在对应代码块的前面即可。这样划分函数中的代码也有利于抽象出更小的函数。
每个程序员都有自己喜欢的格式规则,但如果在一个团队中工作,就是团队说了算。
个人感悟
第六章 对象和数据结构
过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数,面向对象代码便于在不改动既有函数的前提下添加新类。 过程式代码难以添加新数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类。
过程式
和面向对象
没有好坏,根据具体的工作性质选择合适的即可。
note: 说实话,隐藏实现的思路没看太懂┓( ´∀` )┏
得墨忒耳律
得墨忒耳律认为,类C的方法f 只应该调用以下对象的方法:
- C
- 有f 创建的对象
- 作为参数传递给f 的代码
- 有C 的实体变量持有的对象
方法不应调用有任何函数返回的对象的方法。换言之,只跟朋友谈话,不和陌生人谈话。
也可以说是,只调用和类C 关联
或依赖
或自身
的方法。
避免以下代码:
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
第七章 错误处理 第八章 边界
略过
第九章 单元测试
构造-操作-检验(BUILD-OPERATE-CHECK)模式。
- 构造测试数据
- 操作测试数据
- 检验操作是否得到期望的结果
最重要的要求:可读性、可读性、可读性
,维护、维护、维护
。 测试代码和生产代码一样重要,它需要被思考、被设计和被照料。
它该像生产代码一般保持整洁。
反思
之前,基本也是按这些规则写的单元测试,除了维护
,基本上平均每个函数有对应3个测试用例(成功,失败,异常,边界情况),
但是因为很多都是涉及数据库相关的操作,在测试对应函数前,需要先初始化一个数据库环境(主要是数据)。
而你插入数据后必定需要查询对应数据变更是否符合预期,所有就需要很多与数据库操作相关的测试代码。
而且,还得模拟数据库的数据,为测试准备甚至不止一份的数据。最后,单元测试确是变得不太好维护了。修改对应函数时,
需要对应维护这个函数涉及的数据库表的数据。关于这个问题,其实我一直都有关注,但是至今还没有得到非常好的思路。
2023 更新:把数据库操作包装成 DAO
(Data Access Object)对象,然后在单元测试中Mock
DAO
中测试函数使用的方法,
使得跑单元测试时并不依赖具体的数据库,对于算法模型的处理也类似。
第十章 类
还是短小、短小、短小
。
单一权责原则(SRP)认为,类或模块应有且只有一条加以修改的理由。这个应该算是OOP设计中最为重要的概念之一了。
但是,我基本上在开始设计 、构思和写代码之前都不会刻意去考虑这个原则,只有在blame
别人代码时才想到这个原则😬。
不过,幸好我还是有自己的一套设计方法,而且,竟然和Bob 大叔
描述的差不多,即10.2.3 保持内聚性就会得到许多短小的类
。
基本步骤:
- 先按
3.12 如何写出这样的函数
中的方法,先试着写个大函数。其实都不用等写完这个大函数,你基本就有相应的拆分成小函数的欲望了, 再加上单元测试的驱动,拆分成为必然。 - 在将大函数拆成多个小函数时往往就需要传递相应的参数变量,你还会发现,这些小函数还往往有多个相同的参数变量, 这个时候就是创建一个类的时候了(其实就是创建了一个命名空间,使得这些函数共享这些变量)。
- 最后,再加上单元测试的驱动,看看能不能改进一下,是不是有的函数不太好做单元测试,如果不好做单元测试,说明代码还有改进的地方。
想不到,我自己的一套居然和Bob 大叔
的如此相似。
第十二章 迭进
- 运行所有测试
- 不可重复
- 表达了程序员的意图
- 尽可能减少类和方法的数量