《Code clean》读书笔记
计算机源代码最终目的是将人类可读文本翻译成为计算机可执行的二进制指令。
看Code Clean这本书的时候,大多数内容都受益匪浅,但是感觉有些点会很”魔怔“,或者说强迫症福音式的点,追求完美。但是《重构:改善既有代码的设计》这本书里还提到了一个观点:重构不是一个一蹴而就的事,需要长期的实践和经验才能够完成得很好。重构强调的是变得更好,那在此之前我们首先需要先动起手来搭建我们的系统,而不要一味地“完美主义”。
所以《code clean》这本书不是给定写代码的标准,更像是,为写出better code,提供了一些建议。
下面是最近都这本书的一些读书笔记。
关于函数
1.第一要义是短小,第二是更短小。
函数应该达到的短小程度:保证每一个函数都只说一件事情,而且,每一个函数都依次把你带到下一个函数。 if语句、else语句和while语句等,其中的代码块应该只有一行,改行一般应该是一个函数的调用语句。这样不仅能保持函数短小,而且快内调用的函数有较具说明意义的名称,会让可读性增强。
2. 只做一件事:函数应该做一件事,做好这件事,只做这一件事。
3. 参数越少越好
最好的函数参数格式是1,其次是2,避免3个及以上的参数
4.每一个函数一个抽象层级
高、中、低抽象层级的代码混在在同一个函数中会让可读性变差,还可能导致“破窗效应”。同时应该不同的抽象层级函数应该符合”自顶向下原则“:每一个函数后面都跟着位于下一抽象层级的函数。
5.使用描述性的名称:
不管是公有还是私有方法都应该取同样具有描述性的名称,函数越短小、功能越集中,就越便于取一个好名字。 不要害怕长名称,长而具有描述性的名称比短而令人费解的名称好,同时具有描述性的名称比描述性的注释好。
6.抽象化
提高函数(代码)的适用范围,举个例子,就是永远不要写一个 “炸掉地球” 的函数。应该写一个 “炸掉行星” 的函数,然后把地球作为参数放进去。另一个比较好的例子是linus在TED讲的那样,https://www.ted.com/talks/linus_torvalds_the_mind_behind_linux (视频14:10)
7.分隔指令与询问
利用数中原话”函数要么做什么事情,要么回答什么事,但是两者不可兼得“,比如下面这个例子
//反例
//该函数指定设置某个属性,如果成功,就返回true,如果不存在就返回false
public boolean set(String attribute, String value)
//这样操作的时候就容易产生歧义
if (set("username","unclebob"))...
//正例
//正确的做法是将判断和操作分开
if (attributeExists("username")) {
setAttribute("username", "unclebob");
}
8.抽离 try-catch 结构
这样函数的主要逻辑和错误捕获会更好分离。
关于命名
如果一个名称的本身需要注释来补充,那这个命名就不是名副其实
1.变量、函数或类的名称要体现出:它为什么会存在、做什么事、应该怎么用
2.做有意义的区分:
比如ProductInfo或ProductData类,虽然名称不同但是意思无区别。废话都是冗余。Variable一词永远不应当出现在变量名中。Table一词永远不应当出现在表名中。
3.类名和对象名应该是名称或名称短语。类名不应当是动词
关于注释
-
注释越少越好,尽量用代码和命名传递含义
//反例 //判断员工是否有资格享受全额福利 if ((employee.flags & HOURLY_FLAG) && (emplyee.age > 65)) //正例:用命名去传递含义 if (employee.isEligibleForFullBenefits())
-
避免不必要的注释:
比如版本控制工具都有的东西时间作者等,也没必要每个变量都注明是干什么的(随着代码的修改这些可能会失真)。
-
注意维护注释的时效性
不要改了代码不改注释
-
注释要体现代码之外的含义
格式
垂直格式
- 类尽量短小
- 不同的操作、概念之间用空白行隔开
- 垂直方向上,关系密切的概念应该靠近的
- 垂直顺序被调用的概述应该放在执行调用的函数下面,形成一个自顶向下的结构
水平格式
- 可以参考阿里格式规范
对象和数据结构
-
数据结构暴露数据,没有明显行为。
过程式代码操作数据结构,添加新的函数无需修改数据结构,但添加新的数据结构需要修改所有函数。
-
对象暴露行为,隐藏数据。
面向对象式代码操作对象,添加新的类无需修改既有函数,但添加新的函数需要修改所有类。
同时也不应对任何一种操作抱有成见,根据具体情况使用。
迭进
Kent Beck关于简单设计的四条规则,优先级从高到低
-
运行所有的测试
- 紧耦合的代码难以编写测试,测试写的越多,就越会遵循依赖注入之类的规则,使代码加减少耦合。
- 测试消除了对清理代码就会破坏代码的恐惧。
-
不重复
- 两个方法提取共性到新方法中,新方法分解到另外的类里,从而提升其可见性。
- 一种移除重复的通用方法叫”模板方法“
// 原始逻辑
public class VacationPolicy() {
public void accrueUSDivisionVacation() {
//funA;
//funBUS;
//funC;
}
public void accrueEUDivisionVacation() {
//funA;
//funBEU;
//funC;
}
}
// 模板方法模式重构之后
abstract public class VacationPolicy {
public void accrueVacation() {
funA();
funB();
funC();
}
private void x() {
//do A;
}
abstract protected void y() {
}
private void z() {
//do C;
}
}
public class USVacationPolicy extends VacationPolicy {
protected void B() {
//do US B;
}
}
public class EUVacationPolicy extends VacationPolicy {
protected void B() {
//do EU B;
}
}
-
表达程序员的意图
-
减少类和方法的数量
上述很多方法来让人写出比较好的代码,但是也容易陷入“过度设计”,“教条主义”的怪圈中,比如有些编码标准中提到要为每个类创建接口。正如软件工程中经常说到的一个词“没有银弹”,所以也就不存在绝对合理的设计。