代码整洁之道CleanCode
代码整洁之道 Clean Code
有意义的命名
设置可读性高的名称
反例:
int d; //消逝的时间,以日计
正例:
int daysSinceCreation; //详细,一目了然
避免误导
反例:用accountList来指称一组账号,除非它是List类型,否则容易让人误解
做有意义的区分
反例:
getActiveAccount();
和getActiveAccountInfo()
如果没有明确约定,很容易混淆两个方法,不知道调用哪个
读得出来的名称
反例:
genymdhms();
很难读出来,妨碍讨论的流畅性
正例:
generationTimestamp();
一目了然且能读出来
使用可搜索的名称
正例:WORK_DAYS_PER_WEEK
反例:e、i、j等常用短名称
名字长短应和作用域相对应,如果变量或常量可能在代码中多处使用应赋予便于搜索的名称
避免思维映射
举例:命名为i、j、k的变量容易在脑中映射为循环计数器
类名
类名应当是名词,不应该是动词
方法名
方法名应该是动词或动词短语,如deletePage(),应当言简意赅,表达清楚
每个概念对应一个词
比如:controller、manager、driver,在行为上很相似,应当约定某个概念用什么词
别用双关语
避免将同一单词用于不同目的。
比如:add和append。若在多个类中都有add方法,add方法通过增加或连接两个现存的值来产生新值,现在如果要写个新类,新类中有个方法,把单个参数放到collection中,应该用add吗?
这里应该用append,保持add的语义一致。
使用解决方案领域名称
如getCollectorFactory 对应工厂模式
不要添加没用的语境
在一个叫GSD的应用程序中创建一堆带GSD前缀的类,如GSDAccountAddress,不会帮助理解,只会妨碍搜索
函数
函数应该尽量短小, 长度为20行的可读性最佳;
尽量符合单一职责原则,如何判断:看是否能再拆出一个函数,该函数不仅只是单纯地重新诠释其实现;
函数中的语句要在同一个抽象层级, 遵循自顶向下读代码的规则,让每个函数后面都跟着位于下一抽象层级的函数;
switch函数运用:
反例:
public Money calculatePay(Employee e) {`
switch(e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
}
}
违反单一职责原则和开放闭合原则,可以使用抽象工厂模式包装;
尽量使用描述性的名称,容易让人理解,如:includeTeardownPages;
函数参数如果需要三个或三个以上就需要封装成类,否则既妨碍阅读,也让测试变得复杂不便;
取好函数名称,减少记忆参数顺序的负担,如:assertExpectedEqualsActual(expected, actual);
遵守单一职责原则,让函数做到无副作用,减少耦合度,否则bug的缘由可能出现在一个你想不到的函数里;
消灭重复代码,适当地封装成方法;
分隔指令和访问,if(set(username, "unclebob"))
是在问username是否之前被设为unclebob,还是问username是否成功设为unclebob?下面这个更好
if(attributeExists(username)) {
setAttribute(username, "unclebob");
}
注释
能用代码来阐述就不用注释,如:if(employee.isEligibleForFullBenefits)
能够顾名思义;
哪些是好注释?
法律信息(版权及著作权声明)的注释;
提供信息的注释,如讲明正则表达式匹配什么字符串的注释;
对意图的解释,如有些地方的写法比较奇怪但又迫不得已,可以解释一下;
阐释某些难理解的参数和返回值,如:assertTrue(a.compareTo(a)==0);//a==b
适用于不好改动的远古代码;
对某些特殊代码块的警示,如某个测试方法会跑特别久;
TODO注释;
哪些是坏注释?
自说自话,表达不清的注释;
多余、冗长的注释,能用命名讲清楚的就不要加废话注释;
误导性注释;
日志式的注释,大家要活用版本控制系统;
括号后面的注释;
注释掉的代码;
HTML注释;
格式
保持统一的格式,加强可维护性、扩展性
名称要在保证一目了然的前提下尽量简单;
垂直格式
每个函数之间用空白行隔开;
被调用的函数应该在执行调用的函数下面;
横向格式
做好代码前的缩进;
运算符项之间用空格隔开;
将规则写进IDE的代码格式检查功能,善用插件;
对象和数据结构
对象和数据,概念要分清;
两者具有反对称性,过程式代码难以添加新的数据结构,因为它要修改所有相关函数,面向对象的代码难以添加新的函数,因为他要修改所有受影响的类;
迪米特法则(The Law of Demeter):方法不应调用由任何函数返回的对象的方法,
String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
这样会混淆代码和数据结构。对象应该曝露行为,隐藏数据,而数据结构应该曝露数据,没有明显的行为;
最精炼的数据结构只有公共变量、没有函数,被称为数据传送对象。