《代码整洁之道-下》
味道和启发
注释
C1.不恰当的注释
让不恰当的注释保存到源代码控制系统。注释只应该描述有关代码和设计的技术性信息。C2.废弃的注释
过时、无关或不正确的注释就是废弃的注释不应该保留必须马上删除。C3.冗余的注释
注释应该谈及代码自身没提到的东西,否则就是冗余的。C4.糟糕的注释
值得编写的注释必须正确写出最好的注释,如果不是就不要写。如果要编写一条注释,就花时间保证写出最好的注释,字斟句酌。C5.注释掉的代码
注释掉的代码必须删除。它污染了所属的模块,分散了想要读它的人的注意力。
看到注释掉的代码,就删除它!别担心,源代码控制系统会记得它。
环境
E1.需要多步才能实现的构建
构建系统应该是单步的小操作。E2.需要多步才能实现的测试
只需要单个指令就可以运行所有单元测试。函数
F1.过多的参数
函数参数应该越少越好,尽量避免有 3 个参数 的函数。F2.输出参数
输出参数违反直接,抵制输出参数。F3.标识参数
布尔值参数令人迷惑,应该消灭掉。F4.死函数
永不被调用函数应该删除掉。一般性问题
G1.一个源文件存在多个语言
尽量减少源文件语言的数量和范围。G2.明显的行为未被实现
遵循“最少惊异原则”,函数或者类应该实现其他程序员有理由期待的行为,不要让其他程序员看代码才清楚函数的作用。G3.不正确的边界行为
代码应该有正确的行为,追索每种边界条件并进行全面测试。G4.忽视安全
关注可能引起问题的代码,在构建成功后要尽快重新打磨,注重安全与稳定。G5.重复
消除重复代码,使用设计模式和封装。G6.在错误的抽象层级上的代码
抽象类和派生类概念模型必须完整分离,所有较低层级概念放在派生类中,所有较高层级概念放在基类中,例如:与实现细节有关的代码不应该在基类中出现。G7.基类依赖于派生类
将概念分解到基类和派生类的最普遍的原因是较高层级基类概念可以不依赖于较低层级派生类概念。这样,如果看到基类提到派生类名称,就可能发生了问题。基类应该对派生类一无所知。G8.信息过多
类中的方法,变量越少越好,隐藏所有实现,公开接口越少越好。G9.死代码
找到并删除所有不被调用的代码。G10.垂直分隔
变量和函数的定义应该靠近被调用代码。G11.前后不一致
函数参数变量应该从一而终,保持一致,让代码便于阅读和修改。G12.混淆视听
没用的变量,不被调用的函数,没有信息量的注释应该清理掉。G13.人为耦合
不互相依赖的东西不该耦合。G14.特性依恋
类的方法应该只对自身的方法和变量感兴趣,不应该垂青其他类的方法和变量。G15.选择算子参数
避免布尔类型参数,使用多态代替。G16.晦涩的意图
代码要尽可能具有表达力,明白的意图比高效和性能重要。G17.位置错误的权责
“最少惊异原则”,把代码放在读者想到的地方,而不是对自己方便的地方。G18.不恰当的静态方法
如果要使用静态方法,必须确保没机会打算让它有多态行为。G19.使用解释性变量
把计算过程打散成一系列命名良好的中间值使程序更加可读性。G20.函数名称应该表达其行为
如果你必须查看函数代码的实现(或文档)才知道它是做什么的,就该换个更好的函数名,或者重新安排功能代码,放到较好名称的函数中。
G21.理解算法
好多逻辑混乱代码的出现,是因为设计者没有花时间去理解算法,硬塞进大量的if语句和标识勉强让系统能工作,从不真正停下来考虑发生了什么。
当你认为自己完成某个函数之前,请确认自己理解了它是怎么工作的。通过全部测试还不够好,你必须知道解决方案是正确的。
G22.把逻辑依赖改为物理依赖
依赖应该是明显而不应该是假设的依赖。G23.用多态替代 If/Else 或 Switch/Case
首先,多数人使用switch语句,因为它是最直截了当又有力的方案,而不是因为它适合当前情形。这给我们的启发是使用switch之前,先考虑多态是否可以实现。
其次,函数变化甚于类型变化的情形相对罕见,每个switch语句都值得怀疑。
G24.遵循标准约定
每个团队都应该遵循基于通用行业规范的一套编码标准。
G25.用命名常量替代魔术数
在程序中避免使用魔术数,必须使用命名常量代替。
G26.准确
代码中的含糊和不准确要么是意见不同的结果,要么源于懒散,都必须消除。G27.结构甚于约定
坚守结构甚于约定的设计决策。例如,用到良好命名的枚举switch/case要弱于拥有抽象方法的基类。
G28.封装条件
把条件封装成方法。
例如
if(shouldBeDeleted(timer))
要好于
if(tiemr.hasExpired() && !timer.isRecurrent())
G29.避免否定性条件
使用肯定性条件。
if(buffer.shouldCompact())
要好于
if(!buffer.shouldNotCompact())
G30.函数只该做一件事
每个函数只做一件事。
G31.掩蔽时序耦合
创建顺序队列暴露时序耦合,每个函数都产生一下函数所需参数,就可保障正确的时序。G32.别随意
代码不能随意,需要谨慎考虑。
如果结构显得太随意,其他人就会想修改它。如果结构自始至终保持一致,其他人就会使用它,并且遵循其约定。
G33.封装边界条件
例如:+1 或-1 操作必须封装起来。G34.函数应该只在一个抽象层级上
封装不在一个抽象层级上的代码,保持每个函数只在一个抽象层级上。G35.在较高层级放置可配置数据
把配置数据和常量放到基类里。G36.避免传递浏览
“得墨忒耳律”,编写害羞代码,让直接协作者提供所需的服务,而不要逛遍整个系统。JAVA
J1.通过使用通配符避免过长的导入清单J2.不要继承常量
J3.常量 VS.枚举
使用枚举 enum 代替常量。
名称
N1.采用描述性名称名称对应可读性有 90%的作用,必须认真命名。
N2.名称应与抽象层级相符
不要取沟通实现的名称:取反映类或函数抽象层级的名称。
N3.尽可能使用标准命名法
N4.无歧义的名称
N5.为较大作用范围选用较长名称
N6.避免编码
不应该在名称中包含类型或范围的信息,例如:m_,f 等前缀。
N7.名称应该说明副作用
名称应该说明类、变量或函数的所有信息,不应该隐藏副作用。
测试
T1.测试不足保证足够的测试。
T2.使用覆盖率工具
覆盖率工具可以更好地找到测试不足的模块、类、函数。
T3.别略过小测试
T4.被忽略的测试就是对不确定事物的疑问
用@Ignore 表达我们对需求的疑问。
T5.测试边界条件
边界判读错误很常见,必须测试边界条件。
T6.全面测试相近的缺陷
缺陷趋向于扎堆,如果在函数中发现一个缺陷,那么就全面测试这个函数。
T7.测试失败的模式有启发性
你可以通过测试失败找到问题所在。
T8.测试覆盖率的模式有启发性
通过测试覆盖率检查,往往可以找到测试失败的线索。
T9.测试应该快速
慢测试会导致时间紧时会跳过,导致可能出现问题。