函数是所有程序中的第一组代码。
3.1 短小
- 函数的第一条规则是短小,第二条规则是还要更加短小。
- 函数20行封顶最佳。
- 代码块和缩进:代码块应该只有一行,缩进层级不应该多于一层或二层。
3.2 只做一件事
函数应该做一件事情。做好这件事情。只做这一件事。
一件事是指该函数名下的同一抽象层上的事情,可以通过判断是否能在拆出一个函数来判断是否只做一件事。
只做一件事情的函数无法被合理切分为多个区段。
3.3 每个函数一个抽象层级
自顶向下读代码:向下规则——函数后面都跟随着下一个抽象层级的函数,使人在阅读代码的时候,能循抽象层级向下阅读。
3.4 switch语句
写出短小的switch语句很难,写出只做一件事的switch语句也很难。不过通过确保每个switch都埋藏在较低的抽象层级,而且永不重复,可以通过多态来实现。
3.5 使用描述性的名称
- 函数名称要较好的描述函数所做的事,函数中的方法也是如此。
沃德原则:如果每个例程都让你感到深合已意,那就是整洁代码。 - 函数越短小,功能越集中,越便于取个好名称,长而具有描述性的名称更加好。
- 选择描述性的名称能清理你关于模块的设计思路,并帮你改进之。
- 命名方式要保持一致。
3.6 函数参数
最理想的参数数量为零,随参数个数增加而次之,尽量避免三参数函数。
函数参数不易对付,它具有太多概念性,阅读代码时不易理解。
从测试角度看,多参数函数的覆盖所有可能值的组合简直令人生畏。
输出参数比输入参数还要难以理解,不符合人的惯性逻辑。
3.6.1 一元函数的普遍形式
一是操作该参数,二是事件(有输入参数而无输出参数)。不要使用输出参数,输出结果应该体现在返回值上。
3.6.2 标识参数
标识参数丑陋不堪。向函数传入布尔值简直是骇人听闻的做法,方法名会变得很复杂,该函数不止只做一件事。
3.6.3 二元函数
二元函数的参数最好是单个值的有序组成部分,参数的位置可能会影响到使用。
3.6.4 三元函数
三元函数更加难懂,排序、琢磨、忽略的问题都会加倍体现。
3.6.5 参数对象
如果函数看来需要2个、3个或3个以上参数,就说明其中一些参数应该封装为类了。
3.6.6 参数列表
有可变参数的函数可能是1、2、3元,超过数量容易犯错。
3.6.7 动词与关键字
对于一元函数,函数和参数应该形成一种非常良好的动词/名词对形式。
函数名称关键词形式,把参数名称编码成函数名。
3.7 无副作用
- 副作用是一种谎言。函数承诺只做一件事情,但还可能会做其他被藏匿起来的事,这样子容易造成时序性耦合及书序依赖,应尽量避免。
- 输出参数:应避免使用输出参数,因为那样子会耗费检查函数名的时间,降低效率,如果函数需要修改某种状态,通过修改其所属对象属性即可。
3.8 分隔指令与询问
函数要么做什么事情,要么回答什么事,二者不可兼得。
简而言之,将执行指令与判断代码分开。
3.9使用异常代替错误代码
- 从指令式函数中返回错误代码违法了分隔指令和询问的原则,容易造成词意混乱,导致更深层次的嵌套结构。
- 使用异常处理代替返回错误代码,错误处理代码就能从主路径中分离出来。
3.9.1 抽离TryCatch代码
Try/Catch代码块丑陋不堪,搞乱了代码结构,把错误处理和正常流程混为一谈,最好把try和catch代码块主体部分抽离出来,另外形成函数。
3.9.2错误处理就是一件事情
错误处理制作错误处理一件事情
3.9.3 Error.java依赖磁铁
使用异常代替错误代码,新异常就可以从异常类中派生出来,无需重新编译或重新部署。
3.10 别重复自己
DRY原则,DRY是”Don’t Repeat Yourself”的缩写。意思是说,在一个设计里,对于任何东西,都应该有且只有一个表示,其他地方都应该应用这一处。这样需要改动的时候,只需调整这一处,所有的地方就都变更过来了。
重复可能是软件中一切邪恶的根源。
3.11结构化编程
- 结构化编程规则:每个函数、函数中的每个代码块都应该有一个入口、一个出口,意味着在每个函数中只该有一个return语句,循环中不能有break或continue语句,而且永永远远不能有任何goto语句。
- 不过对于小函数,这些规则助益不大,只有在大函数中,才会有明显的好处。
- 只要函数保持短小,偶尔出现的return、break、continue语句并无坏处,甚至比单进单出原则更具有表达力,另外,goto只有在大函数中才有道理,所以应该尽量避免使用。
3.12 如何写出这样的函数
任何人都无法直接写出如此好的函数,它的过程应该是先写,然后测试,按照以上规则打磨、组装好函数。
3.13 小结
遵循这些良好的函数机制,函数会更加好,可以干净利落拼接在一起,形成一种精确而清晰的语言,可以当做故事来讲,这是大师级程序员的做法,把系统当做故事来讲,而不是当做程序来写。