如何写好函数
如何写好函数
短小
函数的第一规则是短小
代码块和缩进
if,else,while语句等,其中的代码块应该只有一行,该行应该是一个函数调用语句。这样不但能保证函数短小,而且,因为块内调用的函数拥有说明性的名称,从而增加了文档上的价值,也说明函数不应该达到足以容纳嵌套结构。所以函数的缩进层不应该多于一层或两层。当然,这样的函数抑郁阅读和理解。
只做一件事
函数就应该做一件事,做好这件事。只做一件事。
如果函数只是做了该函数名下同一抽象层上的步骤,则函数还是只做了一件事。编写函数毕竟是为了把大一些的概念拆分为另一抽象层上的一系列步骤。
所以,判断函数是否不止做了一件事,还有一个方法,就是看是否能再拆出一个函数,该函数不仅是单纯地重新诠释其实现。
函数中的区段
只做一件事的函数无法被合理地切分为多个区段
每个函数一个抽象层级
要确保函数只做一件事,函数中的语句都要在同一抽象层级上。函数中不同抽象层级,往往让人迷惑。可能无法判断某个表达式是基础概念还是细节。
自顶向下读代码:向下规则
要让函数拥有自顶向下的阅读顺序。每个函数后面都跟着位于下一抽象层级的函数。这就叫向下规则
程序就像一系列to起头的段落,每一段都描述当前抽象层级,并引用位于下一抽象层级的后续to起头段落。
switch语句
对于switch语句可以埋到抽象工厂下,不让任何人看到,通过多态的方式来调用。
使用描述性的名称
函数越小,功能越集中,就越容易起个好名字。不一定要很短,长的名字但是能描述功能的也是很好的。命名方式要保持一致。使用和模块名相同的短语名称和动词给函数命名。
函数参数
最理想的参数是零,其次是一,再次是二,应该尽量避免三个或以上参数,当然除了特殊情况。
参数带有太多概念性,参数和函数一般不在一个抽象层级,它要求你了解许多额外的细节。从测试的角度看,参数也让人烦恼。要确保各种参数组合都能正常测试的用例,是很困难的事情。输出参数比输入参数还要难以理解。
一元函数的普遍形式
向函数传入单个参数有两种很常用的理由:
- 有关于该参数的问题
- 需要将参数转换成某些其他的数据
还有种情况就是事件(event),这种情况有输入没有输出。程序将函数看做一个事件,通过参数修改系统状态。
标识参数
向函数传入布尔值用来标识函数不止做一件事情,会大大降低函数的可读性,让人理解起来很困难。
参数对象
如果函数需要三个或以上,就说明其中一些参数应该封装成类了。
动词名词和关键字
对于一元函数和参数应该有一种很好的动词/名词对形式,例如write(name)就很清晰。
有时候我们可以把参数名词作为关键字加入到函数名中,可以减轻记忆参数顺序的负担。(不过对于现在的IDE好像不是很要紧了)
无副作用
副作用是一种谎言。函数承诺只做一件事,但是还会偷偷的做其他的事。有时,会对自己类中的变量做出预期外的改动;有时,会把变量搞成全局变量或者向函数传递的参数。无论哪种情况,都可能导致时序性耦合或者顺序依赖的问题。
分隔指令与询问
函数要修改某对象的状态,或者返回该对象的信息,两件事都干会导致混乱。通过分隔指令,可以使函数的可读性大大增强。
使用异常代替返回错误码
使用异常代替返回错误码可以把错误处理代码从主路径代码中分离出来,得到简化。
抽离TryCatch代码块
最好把try/catch代码块抽离出来形成函数,这样代码就更易于理解和修改。
不要重复
有的算法会在多个函数中重复,参数也不完全一样,代码因此显得臃肿,同时出现错误需要多次修改,应该把常用算法做成泛型算法。
结构化编程
每个函数每个代码块都应该有一个入口一个出口。意味着每个函数中应该只有一个return,循环中尽量少用break或continue,而且除了大函数以外不要用goto。
总结
每个系统都是使用某种领域特定语言搭建,而这种语言是程序员设计来描述那个系统的。函数是语言的动词,类是名词。
大师级程序员把系统当做故事来讲,而不是程序来写。他们使用选定编程语言提供的工具构建一种更为丰富而且更具表达力的语言,用来讲这个故事。
如果你遵守这些规则,函数就会很短小,有个好名字,而且被很好的归置。不过别忘记,真正的目标在于讲述系统的故事,而你编写的函数必须干净利落地拼装到一起,形成一种精确而清晰的语言,帮你讲故事。