碎碎念
这其实是《代码整洁之道》的学习笔记,起了一个看起来很厉害的名字。
之前读过《如何阅读一本书》,但是读完之后还是没什么收获,只懂读,不懂消化。前几天看了这篇文章《一年读100本书的技能,你1分钟就能学得会》,有恍然大悟状。怪不得我一直读书很慢,很少,读了一点就不想读,原来我是一直用的学生考试时代的基础阅读
,这种是最低级的阅读方式,一个字一个字读,漫无目的地读。现在我改用了检视阅读
和分析阅读
,不求甚解,跳过了一些无用章节,比如 Java 相关的,测试相关的,所以一个下午就把大半本书给啃下来了,效率十分高。另外,书的配图很棒
我只挑自己仍需注意的点记录下来,并结合自己的经验谈谈。而一些最基本的,SRP、DRY 等是老生常谈的问题,就不赘述。特别是 DRY,懂的人很多,敲代码的时候能想到的人就少了,想到并且做好的人就更少。工程里面经常看到这段代码复制过来,那段代码复制过去,是最恶心的。这个原则值得用一生去践行、体会并提高
前言
阅读本书有两种原因:第一,你是个程序员;第二,你想成为更好的程序员。
我是第二种。希望有一天,我写的代码,能成为一种艺术
衡量代码质量的唯一有效标准: WTF/min
《代码整洁之道》最有道理的一句话,即代码阅读者每一分钟爆出的”What The Fuck”数量,哈哈哈
使用可搜索的名称
名称长短应与其作用域大小相对应
比如,在一个简单的 for 循环里,使用简单的 i 来命名下标就很足够了;而如果一个变量的作用域很大,那么其命名应该易于搜索(名称长是为了易于搜索)
不要写多余的注释,别让你写的注释比看代码还累
与其花时间编写解释你搞出的糟糕的代码的注释,不如花时间清洁那段糟糕的代码
确实,这点我做得不够,有时候为函数的声明写的注释太多,需要改进
如果可以,尽量别注释代码
其他人不敢删除注释掉的代码。他们会想,代码依然放在那儿,一定有其原因,而且这段代码很重要,不能删除
一方面,我看到注释掉的代码真的很讨厌,因为我是极简主义,能少写一行代码甚至一个空格就少写,一坨被注释的代码放在那里会影响阅读体验
但是另一方面我又不完全同意这个观点,作者说现在已经有版本管理,所以没必要保留注释的代码,但是谁修改代码的时候没事会去查看之前的版本呢?比如,上次我想为频道列表页添加缓存逻辑,但是发现之前有人加过了,但是被注释了,询问之后才发现直接为这个页面添加缓存是有坑的,于是这次添加缓存就顺利绕过了这个坑。那么如果没有这段注释的代码,我可能就会直接跳进这个坑了,因为我修改之前并不可能漫无目的去查看 SVN
不要刻意去水平对齐
刻意的水平像是在强调不重要的东西,分散读者注意力
1 | @property (nonatomic, assign) int leftNum; |
下面这段代码更好,因为注意力能更集中在变量的类型上
1 | @property (nonatomic, assign) int leftNum; |
要编写简洁的代码,必须先写肮脏的代码,然后再清理它
问题是写完了肮脏的可运行的代码之后,别着急开始下一个任务而忘记改进它
使用多个函数,通常优于向单个函数传递某些代码来选择函数行为
先看一个例子
1 | - (void)pay:(BOOL)isVip |
首先作者认为这种代码不好,因为调用的时候应该去记住传递的 BOOL 代表什么意思,应该改为以下这种
1 | - (void)pay |
或这种
1 | - (void)vipPay |
首先方法1并不是处处可行,因为对于 Vip 的判断是否合适放在本类要根据具体情况讨论,合适则方法1可行
而方法2我不敢苟同,因为这首先违背了一种重要的原则——DRY,通用代码在2处地方重复,维护起来十分恶心
同时,在 IDE 日益强大的今天,多写一个参数并不对程序员的记忆有所挑战,即使有,也比重复代码好
代码要尽可能具有表达力
1 | if (curTime >= startTime && curTime <= endTime && !hasShown) // 是否应该展示 |
应该处理为下面这种,这比写注释好
1 | - (BOOL)shouldShow |
得墨忒耳定律(最少了解原理)
A 有一个属性叫 B,B 有一个属性叫 C,C 有一个 doSomething 的函数。现在需要在 A 里面调用 C 的函数。有以下2种写法
方法1,B 把属性 C 暴露在头文件,让 A 可以直接读取
1 | // A.m |
方法2,B 把 C 中的函数封装成接口提供给外部使用
1 | // A.m |
曾经,我不知道方法1和方法2孰优孰劣,甚至更倾向于方法1,因为代码量更少,但读了这本书后,我明白了,方法2更好,为什么?
- 方法1违背了
得墨忒耳定律
,C 不是 A 的属性,而 A 居然要去了解 C 里面的函数 - 假如不止A,还有A1,A2,A3…直接调用了 C 的函数,那么有一天,当 C 修改 doSomething 这个函数名,或者想在 B 和 C 之间再添加一个类 D 的时候,此时方法1就需要修改很多地方,而方法2只需要修改 B 文件