一、 短小
1. 函数的第一规则就是短小,第二规则还是短小。我们都知道函数还有个规则就是最好不要让你的函数长度超过一屏,因为超过的部分需要来回滚动滚轮,读起来不够方便。
文中作者描述了一个例子就是:作者的朋友写的程序里的每个函数都只有两行、三行、或四行长,每个函数都一目了然,每个函数都只表达一件事。而且每个函数都依序把你带到下一个函数,这就是函数应该达到的短小程度。
2. 代码块和缩进
if、else、while等语句的代码块都应该只有一行,即调用函数的语句。这样不仅能保持函数短小,而且,因为块内调用的函数拥有较具说明性的名称,从而增加了可读性。
这也意味着函数不应该大到足以容纳嵌套结构。所以,函数的缩进层级不该多于一层或者两层。
二、 只做一件事
函数应该只做一件事,做好这件事,只做一件事。
要判断函数是否只做了一件事,就是看能否再拆出来一个函数。
三、 每个函数一个抽象层级
自顶向下读代码:向下规则。
让每个函数都调用位于下一抽象层级的函数,这样一来,在查看函数列表的时候,就能寻抽象层级向下阅读。
四、 对函数名使用描述性的名称
函数越短小,功能越集中,就越便于取个好名字。
给函数取名时,别害怕长名称。长而具有描述性的名称,要比短而费解的名称好。长而具有描述性的名称,要比描述性的长注释好。选择具有描述性的名称能清理你关于模块的设计思路,并有助于你改进。
五、函数参数
最理想的参数数量是零(无参函数),其次是一个参数的函数,再次是两个参数的函数,应该避免三个或三个以上参数的函数。
1. 一元函数的普遍形式:
(1) 通常是传入一个参数,然后转换成其他的什么东西,在输出或返回
(2) 通常是传入一个参数,改变什么状态,或者赋值什么的,没有输出和返回
2. 标识参数
要避免向一个函数传入布尔值,因为这样做,方法就变得复杂起来,而且还不止做了一件事。
正确的做法应该是,把一个函数分割成两个,isTrue() 和 isFalse()
六、 参数对象
如果一个函数需要三个及三个以上就说明这个函数就可以封装成类了。
七、 动词与关键字
给函数起个好名字,能较好的解释函数的意图,以及参数的顺序和意图。对于一元函数,函数和参数应当形成一种非常良好的动词/名词对形式。
例如:write(name)就不错,不管传入的name是什么,都要被write。更好的名称是writeField(name),他告诉我们,name是一个 field。
八、 无副作用
副作用是一种谎言。函数承诺只做一件事,但还是会做其他被藏起来的事,有时,它会对自己的类中的变量做出未能预期的改动。
九、分割指令和问询
函数要么做什么事情,要么回答什么事情,但是二者不可得兼。
例如:
public function set(String attribute, String value);
该函数设置某个指定数量,如果成功就返回true,如果不存在那个属性则返回false。这就有可能导致下面的语句:
if (set('username','unclebob'))... 如果只是看的这个的话会有误导性
应该改为:
if (attributeExists('username')){
setAttribute('username','unclebob');
}
十、 使用异常替代返回错误码
从函数中返回错误码违反了指令与查询分割的规则。它鼓励了在if语句判断中把指令当做表达式使用。
例如: if (deletePage(page) == 'ok')
应该使用:
try {
deletePage(page);
} catch (Exception e) {
log(e.getMessage())
}
十一、 抽离try/catch代码块
try/catch代码块搞乱了代码结构,把错误处理与正常流程混为一谈。最好的方法时把try和catch代码块的主体部分抽离出来,另外形成函数。
十二、别重复自己
当一个算法在多个函数里边重复时,识别起来不太容易,因为这几次重复与其他代码混在一起,而且也不完全一样。这样的重复还是会导致代码变得臃肿,而且算法需要修改的时候就要修改多处。
重复可能是软件中一切邪恶的根源。所以遇到重复的代码要无情的重构。
十三、 如何写出这样的函数
写代码的时候和写别的东西很像。在写论文或文章的时候,你先想什么就写什么,然后再打磨它。初稿也许粗陋无序,你就斟酌推敲,修改到直至你心目中的样子。
作者写函数时,一开始都是冗长而且复杂。有太多的缩进和嵌套循环。又过长的参数列表。名称是随意取得,也会有重复的代码。不过作者会不断的打磨自己的代码,通过分解函数、修改名称、消除重复等,有时还拆分类。最后再组装到一起。
刚写函数的时候不用严守上面所述规则,因为得不偿失。