函数必须干净利落地拼装到一起,形成一种精确而清晰的语言,帮助你讲故事。

3.1 短小

3~4行

代码块与缩进

if, switch while语句中只有一行的函数调用,函数的缩进层级不该多于一层或两层。

3.2 只做一件事

如果函数只是做了该函数名下同一抽象层上的步骤,则函数只做了一件事。

  • 同一抽象层只做一件事
  • 无法再拆出一个函数
  • 无法切为多个区段

3.3 每个函数一个抽象层级

高、中、低三个层级

自顶向下规则

TO…, should….

3.4 switch,多个if/else语句

用抽象工厂/多态

3.5 使用描述性的名称

与模块名一脉相承的单词组合

3.6 函数参数

  • 最好没有,避免3个及以上参数
  • 非常不利于测试
  • 输出函数比输入函数还要难以理解

    3.6.1 一元函数的普遍形式

  • 问关于那个参数的问题(返回布尔)

  • 对输入参数转换操作
  • 事件Event

    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 分隔指令与询问

错误:

  1. public Boolean set(String attribute, String value);
  2. if (set("username", "unclebob")) {}

正确:

  1. if (attributeExists("username")) {
  2. setAttribute("username", "unclebob")
  3. }

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等,可读性要求

  1. // V1.0
  2. if (location.href.indexOf('localhost') > -1) {
  3. doSomething();
  4. }
  5. // V2.0
  6. if (isLocalDevEnv()) {
  7. doSomething();
  8. }
  9. function isLocalDevEnv() {
  10. return location.href.indexOf('localhost') > -1;
  11. }