函数必须干净利落地拼装到一起,形成一种精确而清晰的语言,帮助你讲故事。
3.1 短小
代码块与缩进
if, switch while语句中只有一行的函数调用,函数的缩进层级不该多于一层或两层。
3.2 只做一件事
如果函数只是做了该函数名下同一抽象层上的步骤,则函数只做了一件事。
- 同一抽象层只做一件事
- 无法再拆出一个函数
- 无法切为多个区段
3.3 每个函数一个抽象层级
高、中、低三个层级
自顶向下规则
TO…, should….
3.4 switch,多个if/else语句
用抽象工厂/多态
3.5 使用描述性的名称
与模块名一脉相承的单词组合
3.6 函数参数
- 最好没有,避免3个及以上参数
- 非常不利于测试
-
3.6.1 一元函数的普遍形式
问关于那个参数的问题(返回布尔)
- 对输入参数转换操作
-
3.6.2 不要用标识参数
丑陋不堪。
true做什么,flase做什么;传入参数做什么,不传(null)做什么3.6.3 二元函数
用于有自然排序组合的参数,如坐标
- 把参数改为成员作为调用者
- 把参数作为当前类的成员变量,无需再传递
3.6.4 三元函数
尽量不用3.6.5 参数对象
把一些参数封装为类,例如(x, y)封装为Center类3.6.6 参数列表
什么是可变参数
args
相当于List,成员被同等对待,可视为一个参数。3.6.7 动词与关键词
函数名/参数=动词/名词对形式
函数名词关键字形式:辅助记忆参数顺序:assertExpectedEqualsActual(expected, actual)
函数名和参数的强烈结合,可帮助减小参数名的长度,帮助记忆参数(顺序)。
3.7 无副作用
尴尬的时序性耦合
checkPassword只能在特定时刻调用,验证密码和初始化会话
checkPassword可以改为checkPasswordAndInitializeSession,虽然违反了只做一件事。
输出参数
如果函数必须要修改某种状态,就修改所属对象的状态(而不是参数的状态)。this.appendFooter(s)
-> report.appendFooter()
3.8 分隔指令与询问
错误:
public Boolean set(String attribute, String value);
if (set("username", "unclebob")) {}
正确:
if (attributeExists("username")) {
setAttribute("username", "unclebob")
}
3.9 使用异常代替返回错误码
- 当返回错误码时,就是在要求调用者立刻处理错误
- 使用异常替代错误码,从主路径代码中分离出来
3.9.1 抽离try/catch代码块
从主体抽离出来形成函数3.9.2 错误处理就是函数的一件事
try作为函数的第一个单词3.9.3 Error.java依赖磁铁
使用异常替代错误码,新异常就可以从 异常类 派生出来,无需重新编译或重新部署。
3.10 不要重复自己
也就是封装重复的代码
3.11 结构化编程
每个函数只有一个return语句,循环中不能有break或continue语句,永远不能有任何的goto语句
小函数可以有return, break, continue,不能有goto
3.12 如何写出这样的函数
并不需要一开始就按照规则写,重构
补充
1. 语句要有可读性
if/else,while等,可读性要求
// V1.0
if (location.href.indexOf('localhost') > -1) {
doSomething();
}
// V2.0
if (isLocalDevEnv()) {
doSomething();
}
function isLocalDevEnv() {
return location.href.indexOf('localhost') > -1;
}