在《重构》中,有一个代码坏味道叫 过长的函数。这听起来真是一个不负责任的描述,多长的函数才算长?老马觉得长,可能Kent Beck觉得还好。你觉得不长,你的Pair硬要说太长了,互相谁也说服不了谁,老马真是个爱挑是非的人,引发这么多程序员之间的战争......
对于这个坏味道,很多人往往会陷入几个误区。
误区一:这个好办,我规定一下最大的行数
甚至有人走极端,函数不能超过5行,或者不能超过10行,以至于在不得已超过这个行数的时候,为了较少行数而使用晦涩难懂的三元运算符,或者不换行。这种做法,只取其型,而忽略其本,本末倒置,让代码可能越来越难理解。代码行数是一个参考标准,决不能作为一个硬性指标,因为你很难去定这个标准。
误区二:长怕什么,我用注释来区分
如果因为逻辑过多,明知读者看着会费劲,有意添加注释来解释,这种注释就是一种遮羞布的存在了。这个时候其实就是一个潜在信号,你可以将这里的代码抽取成一个独立函数的,通过有意义的命名来表达其意图。这样自然会让原本一个长函数变短两个函数,行数自然就变短了。
除了这种线性逻辑,让你有添加注释的冲动,是一种潜在信号。另外还有一种信号是,当存在switch或者if-else,并且分支逻辑比较复杂的时候,也可以考虑将每个分支的抽取成单独的函数。
误区三:我在名字上用And来命名,长点也不怕
有人说,我的名字命名清楚就好,虽然长点,你也不用看实现细节,也不太影响。这种需要通过And连词来命名的函数,这个函数明摆的就是做了不止一件事情,通常建议不要用And来连接一个函数多种行为,这个时候,建议通过职责更加单一的函数来封装逻辑。
有时候你不想用And,但又很难给一个合适的名字的时候,尤其是为了减少循环,在一个循环中做了多件事情,很有可能循环中干了多件事情。这个时候将循环提炼出来,然后将职责分开,当然每个单独的函数要做一次遍历,通常,除非多次遍历影响了性能要求,这个职责分离是值得去做的。
那到底如何判断长?
行数只能是一个参考,绝不应该成为一个硬性指标。这背后的一个核心指导原则是上下文边界,在函数层面就是职责,即这个函数所做的事情。一个函数只做一件事情,你走极端地去执行这条原则也不过分,力求让每个函数干脆利索,专注精干,做好这一点,很多长函数便不再是长函数了,至于那种就是一件事情要写很多行代码的,也没办法拆分了,也只能保持这个长度了。
通过有意义的命名,让函数所做的事情的细节隐藏在函数体内,在调用方,通过一系列名字的意图让别人在陷于细节之前就能明白当前的业务逻辑。
分离上下文边界,对程序员的要求是较高的 -- 对业务的充分理解,通过业务分析,识别每一个独立而又有关系的上下文。这些是需要在不断练习和反馈中提升的能力。从最简单的函数职责拆分,是一个不错的开始。
网友评论