[toc]
最近在阅读《Clean Code》,读书笔记记录于此。
什么样的代码是好代码?
- 好代码让人赏心悦目
- 完成功能 - 基本要求
- 划分合理 - 低耦合、高内聚
- 风格规范 - 代码易阅读、易维护
- 实现高效 - 性能好
- 简洁实用 - 避免过度设计、避免炫技
命名
- 使用读得出来的名称
- 避免过度缩写,例如genymdmhs
- 名称的长短应与其作用域大小一致
- 越是作用域大的变量的名字越应该清晰地描述其含义,便于搜索
- 避免使用编码
- 无需将类型或作用域等编码进变量名中,如 f_price, g_count, m_name…,否则变量类型修改之后,变量名也需要跟着更新,否则就会误导读者
函数
- 函数的第一规则是要短小;第二规则是还要更短小
- 最好不要超过100行,20行封顶最佳
- 避免过多嵌套:函数的缩进层级不应该多于1层或2层
- 函数应该做一件事。做好这件事。只做这一件事。
- 函数参数越少越好,应尽量避免三个及以上的参数。
- 参数数目越少,测试时需要覆盖的场景数越少,越方便测试
- 尽量避免使用参数进行信息输出,最好直接用返回值进行输出
- 有时候返回值比放在参数中多了一次拷贝??? RVO, NRVO
- 使用异常代替返回错误码 ?
- 忘了在哪本书里面看的,说C++里面尽量避免使用异常??感觉本书主要针对java编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29try{
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
catch(Exception e) {
logger.log(e.getMessage());
}
// 上述代码 错误处理与正常流程被混为一谈。。。
public void delete(Page page) {
try{
deletePageAndAllReferences(page);
}
catch(Exception e) {
logError(e);
}
}
private void deletePageAndAllReferences(Page page) throws Exception {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e) {
logger.log(e.getMessage());
}
// 上述拆分在java中固然好。。但在C++中就不能不在每个子函数内部添加try{}catch(){throw...}
- 忘了在哪本书里面看的,说C++里面尽量避免使用异常??感觉本书主要针对java编程
- 从一开始就尽量遵照规则写好每一个函数?还是先完成功能,再打磨、重构、组装成遵循规则的函数?
注释
- 注释本身就是一种失败?是一种代码表达能力低的表现?
- 注释不能美化糟糕的代码;尽量用代码来阐述;避免不必要的注释
- 不必为了注释而注释
- 注释也会说谎,尤其当注释没能被持续维护的情况下
格式
- 纵向格式
- 不同的思路或表达之间应该用空白行区隔开
- 紧密相关的代码应该相互靠近
- 若某个函数调用了另外一个,就应该把它们放到一起,而且调用者应该尽可能放在被调用者上面。
- 横向格式
- 一行代码不要超过120个字符
- 根据运算符的优先级进行空格, e.g. a*b + c*d vs. a * b + c * d
对象和数据结构
- 对象把数据隐藏于抽象之后,曝露操作数据的接口;数据结构曝露其数据,没有提供有意义的函数。
- 过程式代码(使用数据结构)便于在不改变现有数据结构的前提下添加新函数,面向对象代码便于在不改变既有函数的前提下添加新类。过程式代码难以添加新数据结构,因为必须修改所有函数;面向对象代码难以添加新函数,因为必须修改所有类。实际使用时需要根据情况进行选择(希望灵活地添加数据类型?还是操作行为?)。
- The Law of Demeter:模块不应该了解它所操作对象的内部情形。方法不应调用由任何函数返回的对象的方法。
错误处理
- 错误处理很重要,但如果他搞乱了代码逻辑,就是错误的做法
- 可控异常违反了开放/封闭原则。如果你在方法中抛出可控异常,而catch语句在三个层级之上,你就需要在catch语句和抛出异常处之间的每个方法签名中声明该异常。这意味着对软件较低层级的修改,都将波及较高层级的签名。
- 将第三方api打包是个良好的实践手段。当你打包一个第三方api,你就降低了对它的依赖:未来你可以不太痛苦地改用其他代码库。
- 不要返回或者传递null值。返回null值,等于在给调用者添乱,试想如果有一处没有进行null检查,应用程序就会失控
- 可以抛出异常,或者返回特例
- 如果将错误处理隔离看待,独立于主要逻辑之外,就能写出鲁棒而整洁的代码。