[学习笔记] 《代码整洁之道》—第3章 函数
函数要短小,更短小
- 20行封顶最佳;
- 每一行都不应该太长;
- if, else, while语句等,其中的代码块应该只有一行函数调用语句;
- 调用的函数应该有一个较具说明性的名称。
- 函数不应该大到足以容纳嵌套结构。
- 缩进层级不应该多于一层或两层。
一个函数只做一件事,做好这件事
- 函数只做该函数名下统一抽象层上的步骤。
- 只做一件事的函数:
- 不可能再从中再拆出一个新函数;
- 无法被合理的切分为多个区段。
每个函数一个抽象层级
-
自顶向下读代码:向下规则
-
每个函数后面都跟着下一抽象层级的函数。
void function_A(){ function_B(); } void function_B(){ function_C(); function_D(); } void function_C(){ function_c1(); } void function_c1(){ ... } void function_D(){ ... }
-
switch语句
- 很难写出短小的switch语句(if/else语句同样如是)。
- 将switch语句都埋藏在较低的抽象层级;
- 永不重复,只出现一次,用于创建多态对象。
- 很难100%的执行。
使用描述性的函数名称
- 函数越短小,功能越集中,就越便于取一个好名字;
- 长而具有描述性的名字
- 要比短而令人费解的名字好;
- 要比描述性的长注释好
- 选择描述性的名称有助于
- 理清模块的设计思路;
- 代码的改善重构。
- 命名方式要保持一致
- 使用与模块名一脉相承的短语、名词和动词给函数命名。
函数参数
- 零参数函数最理想,其次是单参数函数,再次是双参数函数,应尽量避免三参数函数(除非有足够特殊的理由)。
- 参数不易对付,概念性太多,阅读不易;
- 从测试角度看,参数更叫人为难;
- 输出参数比输入参数更难以理解。
- 信息从参数输入函数,通过返回值从函数输出。而不是通过参数输出。
一元函数的普遍形式
-
向函数传递单个参数有两种普遍的理由:1. 问关于该参数的问题, 2. 操作该参数。
- 选用较能区别这两种理由的名称,而且总是一直的在上下文中使用这两种形式。
-
还有一种不那么普遍但极有用的单参数函数形式:事件(event)。
- 有输入参数,而无输出参数;
- 将函数看作一个事件,使用该参数修改系统那个状态;
- 谨慎地选用那个名称和上下文语境,让读者很清楚地了解它是个事件。
标识参数
- 标识参数丑陋不堪;
- 向函数传入布尔值简直是骇人听闻。
二元函数
- 有两个参数的函数比一元函数难懂;
- 当然有时两个参数正好;
- 两个参数的前后顺序往往被搞错;
- 尽量利用一些机制将二元函数转换成一元函数。
三元函数
- 三个参数的函数更加难懂,写之前想清楚;
参数对象
-
如果函数看来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。
Circle makeCircle(double x, double y, double radius); Circle makeCircle(Point center, double radius);
参数列表
-
有时,我们想要向函数传入数量可变的参数,例如:
String.format("%s worked %.2f hours.", name, hours);
如果像这样被等同对待,就和类型为List的单个参数没有区别。
动词与关键字
给函数取个好名字
-
能较好地解释函数的意图
-
对于一元函数,函数和参数应该形成一种良好的动词/名词对形式。
write(name)
就很好;更好的大概是
writeField(name)
, 它告诉我们”name“是一个"field"。
-
-
解释参数的顺序和意图
-
把参数名称变成函数名
assertEqual(expeacted, actual)
改成assertExpectedEqualsActual(expeacted, actual)
减轻了记忆参数顺序的负担。
-
无副作用
副作用是一种谎言!
- 函数承诺只做一件事,但还是会做其他被藏起来的事。
- 会导致古怪的时序性耦合及顺序依赖。
- 应尽量避免使用输出参数
- 如果函数必须要修改某种状态,就修改所属对象的状态吧!
分隔指令与询问
函数要么做什么事,要么回答什么事,但二者不可得兼。
- 函数应该修改某对象的状态或是返回该对象的有关信息。
- 两样都干常会导致混乱。
使用异常替代返回错误码
似懂非懂,没看明白, 郁闷,再接再厉!
别重复自己
代码的重复会导致问题
- 代码臃肿;
- 当算法改变时多处需要修改;
- 也增加了多次放过错误的可能性。
重复可能是软件中一切邪恶的根源。
结构化编程
- 只要函数够短小,偶尔出现return,break或continue语句没有坏处,甚至比单入单出原则更具表达力。
- goto只在大函数中才有道理,应该尽量避免使用。
如何写出这样的函数
-
先写代码
- 可能冗长而复杂,可能又过长的参数,名称也不合适,还会有重复;
- 不过会配上一套单元测试,覆盖每行丑陋的代码。
-
然后在打磨这些代码
- 分解函数,修改名称,消除重复;
- 缩短和重新安置方法;
- 保持测试通过。
-
遵循规则,组装好修改后的函数。
并不是从一开始就按照规则写函数。
参考文献
[1] Robert C. Martin 著,韩磊 译,《代码整洁之道》,北京:人民邮电出版社,2010.1(2018.9 重印), ISBN 978-7-115-21687-8。