函数-《代码整洁之道》读书笔记(二)
原文地址:https://liujiao111.github.io/2019/06/19/clean-code-function/
目录:
- 名称命名规则-《代码整洁之道》读书笔记(一):https://liujiao111.github.io/2019/06/18/clean-code/
- 函数-《代码整洁之道》读书笔记(二):https://liujiao111.github.io/2019/06/19/clean-code-function/
函数是代码中必不可缺的程序单元,那怎样的函数才算是好的函数呢?
短小
函数代码行数应该尽量保持少,每个函数都只做一件事,让每个函数看上去都一目了然。
例如,初始代码:
public static String renderPageWithSetupsAndTeardowns(PageData pageData,
boolean isSuite) throws Exception {
boolean isTestPage = pageData.hasAttribute("Test");
if(isTestPage) {
wikiPage testPage = pageData.getWikiPage();
StringBuffer newPageContent = new StringBuffer();
includeSetupPages(testPage, newPageContent, isSuite);
newPageContent.append(pageData.getContent());
includeTeardownPages(testPage, newPageContent, isSuite);
pageData.setContent(newPageContent.toString());
}
return pageData.getHtml();
}
重构后:
public static String renderPageWithSetupsAndTeardowns(PageData pageData,
boolean isSuite) throws Exception {
boolean isTestPage = pageData.hasAttribute("Test");
if(isTestPage) {
includeSetupAndTeardownPages(PageData, isSuite);
}
return pageData.getHtml();
}
代码块和缩进:
if、else、while语句等,其中的代码块应该只有一行,该行大抵是函数调用语句,这样不但能保保持函数短小,,也因为块内调用的函数拥有较具有说明性的名称,从而增加了文档的价值。
函数的缩进层级不应该多于一层或两层,易于阅读和理解。
只做一件事
一个函数应该只做一件事,做好这件事。只做一件事的函数无法被合理地切分为多个区段
每个函数一个抽象层级
要确保函数只做一件事,函数中的语句都要在同一抽象层级上。函数中混杂不同抽象层级往往让人迷惑。
尽量让代码拥有自顶向下的阅读顺序。本篇末尾的代码遵循了这一规则。
switch语句
写出短小的switch语句很难,因为它天生就要做好多事,也包括if/else语句,不过还是得确保每个条件语句都在较低的抽象层级,而且永不重复,这可以利用多态来实现。例如,初始代码:
public Money calculatePay(Employee e) {
switch (e.type) {
case COMMISSIONED:
return calculateCommission(e);
case HOUTLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalaroed(e);
default:
return new InvalidEmplyeeType(e.type);
}
}
该函数的问题:
- 太长,当出现新的雇员类型时,会更长;
- 明显做了不止一件事;
- 违背了单一权责原则;
- 违背了开放闭合原则,因为每当添加新类型时,就必须修改
- 到处都有类似的函数结构:例如isPayDay(Employee e, Date date),或者
deliverPay(Employee e, Money pay);
解决办法:
- 将switch语句埋到抽象工厂下,不让任何人看到
- 工厂使用switch语句为Emplyee的派生物创建适当的实体
代码如下:
Employee:
public abstract class Employee {
public abstract boolean isPayday();
public abstract Money calcalatePay();
public abstract Money deliverPay(Money pay);
}
工厂接口EmplyeeFactory:
public interface EmplyeeFactory {
public Employee makeEmployee(EmplyeeRecord r);
}
工厂实现类
public class EmplyeeFactoryImpl implements EmplyeeFactory {
@Override
public Employee makeEmployee(EmplyeeRecord r) {
switch (r.type) {
case COMMISSIONED:
return CommissionEmployee(r);
case HOUTLY:
return HourlyEmployee(r);
case SALARIED:
return SalaroedEmploee(r);
default:
return new InvalidEmplyeeType(r.type);
}
}
}
使用描述性的名称
函数名称要能极好地描述函数做的事,例如将testTableHtml改为SetUPTeardownIncluder.render。函数越小,功能越集中,就越便于取个好名字
- 别害怕长名称,长而具有描述性, 好的描述性名称能帮助你理清模块设计思路;
- 别害怕花时间取名字;
- 命名方式要统一
函数参数
函数参数越少越好,如果有三个参数及以上,就要考虑函数设计是否合理。
一元函数的普遍形式
函数应该是操作参数或者将其转换为什么东西再输出这些动作,因此名称应该能表达出对参数操作的信息,例如
boolean fileExist(“MyFile”)这样。
标识参数
向函数传入布尔值简直是骇人听闻的做法,千万别这么做,应该把判断后的逻辑写成不同的函数。
二元函数
参数越多,函数越难懂。当然,有些情况下不可避免。应该尽量将其转换为一元函数,例如将某个参数写成成员变量,或者在构造器中传入。
三元函数
三元函数比二元函数难懂的多,排序、琢磨、忽略的问题会加倍出现。要尽量避免
参数对象
如果一个函数需要两个、三个以及以上的参数,就需要考虑将参数封装为类了、
参数列表
可变参数放在最后一个参数位置,其他参数应该尽量保证不超过2个。
动词与关键字
对于一元函数,函数和参数应该形成一种非常良好的动词/名词对形式,例如write(name),就比较好,甚至writeFiled(name)也不错
assertEqual改为assertExpectedEqualsActual(expected, actual)更好。
无副作用
函数承诺只做一件事,但是还会做其他隐藏起来的事情,这应该是要避免的情况。或者在函数名称中说明,或者去除其他隐藏的操作、
避免输出参数
分隔指令与询问
使用异常替代返回错误码
使用异常替代返回错误码,错误处理代码就能从主路径中分离出来,得到简化
抽离try/catch代码块
try/catch代码块丑陋不堪,搞乱了代码结构,把错误处理与正常流程混为一谈,最好把它们抽离成另外的函数
错误处理就是只做一件事
减少重复代码块
结构化编程
总结
写程序应该像是在讲故事,而不是真的当程序来写。
电子书免费共享:链接:
https://pan.baidu.com/s/1wvoRJGonA70J9hFn_w5jwA
提取码: 37jy
上一篇: 注释-《代码整洁之道》读书笔记(三)
下一篇: Python二级笔记