代码整洁之道读书笔记
一、有意义的命名
- 名副其实
表示产品A上线时间剩余天数
// 差
int d;
// 一般, 但和差一样, 不知道天数的作用
int day;
// 好
int remainDaysBeforeOnline;
获取水果订单中所有的id为4的苹果订单
// 差
List<int[]> list1 = new ArrayList<int[]>();
for(int[] x : theList){
if (x[0] == 4){
list1.add(x)
}
}
return list1;
// 好
List<int[]> appleOrders = new ArrayList<int[]>();
for(int[] orderItem : allFruitOrder){
if(orderItem[idIndex] == APPLE_ID){
appleOrders.add(orderItem)
}
}
return appleOrders;
// 更好
把 orderItem[idIndex] == APPLE_ID 抽离成函数
isAppleOrder()
- 避免误导
- 不用非通用的缩写: ht, aix, sco
- 不用accountList 表明账户列表,list 常有特殊意义,除非这确实是一个List类型
- 不使用字母i,o,l 来表示变量,因为会和1,0数字混
- 做有意义的区分
- a1,a2,a3无意义
- 类Product 和 ProductInfo 和 ProductData 无区分,是意义含混的废话。只取Product
- nameString 和 name 无区分,因为name不可能是浮点
- Customer和CustomerObject无区分,取Customer
要区分,就要取读者能够区分的不同单词
- 使用读得出来的单词
// 差
class DtaRcrd102 {
private Date genymdhms;
}
// 好
class Customer {
privateDate generationTimestamp;
}
- 使用可搜索的名称
MAX_GIRL_AGE 就比 8 要好,因为前者可搜索,一般不重复,后者到处都有
- 类名应该是名词或名词短语如Customer, Account, Address 而不应该使用Manager Data Info
- 方法名应该是动词或动词短语如postPayment, deletePage,
- 每一个概念对应一个词
二、函数
- 短小
能有多小,就多小
- 只做一件事,做好这件事
- 每个函数一个抽象层级
getHtml() 较高抽象层
encodePath() 中级抽象层
appendString() 低级抽象层
同一个抽象层的东西,写到一些,不要高级中又大量混入低级。
应该是高级调用中级,中级调用低级,从上到下依次
- 函数参数
参数越少越好,最好的是0参数,第二好的是1参数,超过3个参数,就要反省了
- 标识参数
// 差
render(true);
render(false);
// 中
render(Boolean isSuite);
// 好
renderForSuite();
renderForSingleTest();
- 如果参数超过3个,则要考虑将参数封装为类了
- 动词和关键字
assertEqual(expected, actual); 使用者往往不知道参数的顺序先后。
assertExpectedEqualActual(expected, actual); 这样会更好一些,因为从函数名就可以知道参数先后。
但我觉得现在的ide已经能将参数名显示出来,那简短的名字将更好。
- 分隔指令与询问,函数要么做事,要么回答问题,不要即做事又回答问题。
- 使用异常替代返回错误码
如果使用异常替代返回错误码,那么代码分离,使代码得到简化。如果返回错误码,那得到错误码的人又得处理错误
- 抽离try/catch代码块
// 一般
try{
deletePage(page);
register.deleteReference(page.name)
...
}catch{
...
}
// 好,抽离try/cache
try{
deletePageAndReference(page)
}catch{
...
}
deletePageAndReference(page){
deletePage(page);
register.deleteReference(page.name)
...
}
- 错误处理就是一件事
- 错误码,依赖磁铁,不好
就是定义错误码的类,导致其他的许多类都要导入和使用他,一但他改变,所有地方都要重新编译。而新异常可以从异常类中派生出来,无需要重新编译或重新部署。
- 如何写出优秀的函数
几乎没有可能一开始就写出优秀的函数,高手也是先把函数,逻辑,代码写出来,命名,规则,等都不那么完美。等写完了,再来一步一步分离,拆解,优化,重命名!!!!
三、错误处理
- 使用异常而非返回码
- 别返回null
因为返回了null,则调用者要检查返回是否为null,不然就会报空指针错误。
有错误就应该抛出异常或返回特例对象。
其实可以返回空数组,空字符串等,都比null要好。
- 别传递null值
- 把错误提取,独立处理,别混在正常业务代码中
四、边界
- 学习性测试
在使用第三方库时,我们没必要为第三方库写测试,测试应该由第三方库来负责。但我们又在使用他们的代码。我们可以为我们使用到的部分的第三方库代码做测试。相当于边学习,边测试。
- 使用尚不存在的接口
我认为其实就是把方法造出来,写一下模拟数据,进行mock,当尚不存在的接口开发好后,再接进来。
- 测试代码和生产代码一样重要。
当我们在程序还小的时候,不写测试代码,只是自己执行一下,看看是否有问题。
当程序大的时候,已经没办法一项一项的测试过去。我们修改了一处代码,引发了其他处代码的未知错误。
单元测试越完整,修改才越大胆和快速。不然你总是会害怕改代码。
- 单元测试的好处,让代码可扩展,可维护,可复用
五、类
- 类应该短小
系统应该由许多短小的类组成,而不是少数几个庞大的类组成。