短小
函数应该尽量短小,20行封顶最佳。对于if语句、else语句、while语句等,其中的代码块应该只有一行。该行大抵应该是一个函数调用语句。这样不但能保持函数短小,而且,块内调用的函数能够拥有较具说明性的名称。
只做一件事
如果函数只是做了该函数名下同一抽象层上的步骤,则函数还是只做了一件事(函数中的语句都要在同一抽象层级上)。编写函数毕竟是为了把大一些的概念(换言之,函数的名称)拆分为另一抽象层上的一系列步骤。要判断函数是否不止做了一件事,还有一个方法,就是看是否能再拆出一个函数,该函数不仅只是单纯地重新诠释其实现。
无副作用
函数承诺只做一件事,但还是会做其他被藏起来的事。比如,它会对自己类中的变量做出未能预期的改动(例如:在校验密码的函数中初始化会话),会导致古怪的时序性耦合及顺序依赖。如果一定要时序性耦合,就应该在函数名称中说明。
每个函数一个抽象层级
我们想要让代码拥有自顶向下的阅读顺序。我们想要让每个函数后面都跟着位于下一抽象层级的函数,这样一来,在查看函数列表时,就能循抽象层级向下阅读了。我把这叫做向下规则。程序就像是一系列TO起头的段落,每一段都描述当前抽象层级,并引用位于下一抽象层级的后续TO起头段落。
switch 语句
Switch天生要做N件事,因此尽量避免使用。如果要使用,尽量确保每个switch都埋藏在较低的抽象层级,而且永远不重复。可以通过JAVA的继承和多态避免使用switch,如工厂方法,将原来放在一个类方法中的switch逻辑分散到多个类中。
使用描述性的名称
函数名称能较好地描述了函数做的事,别害怕长名称。命名方式要保持一致。
对于一元函数, 函数和参数应当形成一种非常良好的动词/名词对的命名形式。例如,write(name)。我们也可以把参数的名称编码成了函数名。例如,assertEqual 改成 assertExpectedEqualsActual(expected,actual)。
函数参数
最理想的参数数量是零(零参数函数),其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数),尽量把二元函数和三元函数转化为一元函数。参数会增加函数理解的难度,也会增加测试成本。
输出参数
函数输出参数难以理解,我们惯于认为信息通过参数输入函数,通过返回值从函数中输出,因此尽量避免使用输出参数。如果函数必须要修改某种状态,就修改所属对象的状态来实现输出。
—元函数的普遍形式
一元函数通常是处理该参数的函数。
也可能是操作该参数,将其转换为其他的对象,再输出之。
还有就是程序将函数看作是一个事件,使用该参数修改系统状态。
尽量避免编写不遵循这些形式的一元函数。
标识参数
标识参数指向函数传入布尔值来执行不同的逻辑。这样做,方法签名立 刻变得复杂起来,大声宣布本函数不止做一件事。如果标识为true将会这样做,标识为false 则会那样做。如果函数需要标志参数,应该将函数一分为二。
参数对象
如果函数看来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。从参数创建对象,不仅减少参数数量,还可以将相关参数聚合在一起。
分隔命令与询问
函数要么做什么事(命令),要么回答什么事(询问),但二者不可得兼。函数应该修改某对象的状态,或是返回该对象的有关信息。两样都干常会导致混乱。
异常处理
使用异常替代返回错误码
命令式函数返回错误码(通知该操作是否成功)轻微**违反了命令与询问分隔的规则**。它鼓励了在if语句判断中把命令当作表达式使用。
if(deletePage(page) == E_OK)
当客户端调用多个返回错误码的函数时,代码会有大量的if语句,这会导致深层次的嵌套结构,并且要求调用者立刻处理错误。如果使用异常替代返回错误码,错误处理代码就能从主路径代码中分离出来, 得到简化。
另外返回错误码通常暗示某处有个类或是枚举,定义了所有错误码。这样的类就是一块依赖磁铁,其他许多类都得导入和使用它。当Error枚举修改时,所有这些其他的类都需要重新编译和部署。使用异常替代错误码,新异常就可以从异常类派生出来,无需重新编译或重新部署。
抽离try/catch代码块
不要在一个函数中既有try的处理逻辑又有catch处理逻辑,即把错误处理与正常流程放在一起。最好把try代码块的主体部分抽离出来,另外形成函数。
函数应该只做一件事。错误处理就是一件事。因此,处理错误的函数不该做其他事。即try应该在函数开头,catch/finally代码块后面也不该有其他内容。
避免代码重复
代码重复会降低可读性,因为代码因此而臃肿,且当算法改变时需要修改多处地方。我们可以通过将重复的代码抽离成函数,或者放在基类或者AOP来消除重复。
结构化编程
对于较大的函数、函数中的每个代码块都应该有一个入口、一个出口。遵循这些规则,意味着在每个函数中只该有一个return语句。
小结
好的函数不是一开始就能写出来的,而是经过反复修改不断打磨形成的。