这个代码还算不上已经处理得很整洁了,但至少同之前相比,已经简洁了一些。我们只用了最简单的提取函数这个重构手法,就把一个大函数拆分成了若干的小函数。
长函数往往还隐含着一个命名问题。如果你看修改后的sendChapter,其中的变量命名明显比之前要短,理解的成本也相应地会降低。因为变量都是在这个短小的上下文里,也就不会产生那么多的命名冲突,变量名当然就可以写短一些。
平铺直叙的代码,一个关键点就是没有把不同的东西分解出来。如果我们用设计的眼光衡量这段代码,这就是“分离关注点”没有做好,把不同层面的东西混在了一起,既有不同业务混在一起,也有不同层次的处理混在了一起。我在《软件设计之美》专栏中,也曾说过,关注点越多越好,粒度越小越好。
一次加一点
有时,一段代码一开始的时候并不长,就像下面这段代码,它根据返回的错误进行相应地错误处理:
if (code == 400 || code == 401) { // 做一些错误处理 }
然后,新的需求来了,增加了新的错误码,它就变成了这个样子:
if (code == 400 || code == 401 || code == 402) { // 做一些错误处理 }
这段代码有很多次被修改的机会,日积月累:
if (code == 400 || code == 401 || code == 402 || ... || code == 500 || ... || ... || code == 10000 || ...) { }
后人看到就想骂人。任何代码都经不起这种无意识的累积,每个人都没做错,但最终的结果很糟糕。对抗这种逐渐糟糕腐坏的代码,需要知道“童子军军规”:
让营地比你来时更干净。
Robert Martin 把它借鉴到了编程领域,我们应该看看自己对于代码的改动是不是让原有的代码变得更糟糕了,如果是,那就改进它。
但这一切的前提是,你要能看出自己的代码是不是让原有的代码变得糟糕了,所以,学习代码的坏味道还是很有必要的。
至此,我们看到了代码变长的几种常见原因:
- 以性能为由
- 平铺直叙
- 一次加一点
代码变长根本是一个无意识的问题,写代码的人没有觉得自己把代码破坏了。但只要你认识到长函数是一个坏味道,后面的许多问题就自然而然地会被发掘出来,至于解决方案,你已经看到了,大部分情况下,就是拆分成各种小函数。
总结
没有人愿意去阅读长函数,但许多人又会不经意间写出长函数。
对于团队,一个关键点是要定义出长函数的标准。
过于宽泛的标准没有意义,想要有效地控制函数规模,几十行已经是标准上限,这个标准越低越好。
长函数产生的原因:
- 性能为借口
- 代码平铺直叙
函数写长最常见的原因。之所以会把代码平摊在那里:
- 把多个业务写到了一起
- 把不同层次的代码写到了一起。究其根因,那是“分离关注点”没有做好 - 每人每次加一点点
应对主要办法就是要坚守“童子军军规”,但其背后更深层次的支撑就是要对坏味道有着深刻的认识
把函数写短,越短越好。