动态类型一时爽,代码重构火葬场?谈谈重构
脚本之家
你与百万开发者在一起
If it stinks, change it.
重构是优秀软件开发人员的必备技能,《重构》是 Martin Fowler 送给软件开发人员的一份礼物。
构建可维护的代码
C++ 之父 Bjarne Stroustrup 说:“我喜欢优雅和高效的代码。” Python 之禅说:“美胜于丑。” 作为一名代码洁癖重症患者,我也喜欢简洁优雅、蕴含着编程美学的代码。“如切如磋,如琢如磨。”一段简洁优雅的代码,就像是一个艺术品一般,有着一种独特的魅力。
代码是一种语言,它被我们用来与计算机进行交流。但是,掌握一门编程语言,并不能使我们成为一名出色的软件开发者,正如掌握一门自然语言不能使我们成为一名作家一样。在代码之外,还需要很多技能与能力,这是一个为数不多的需要同时使用左脑(语言、逻辑……)与右脑(创造力、艺术……)的领域。
虽然左右脑也并非是如此泾渭分明,但毋庸置疑的是,编程并非纯理性的,因为出色的代码之中凝结着创造力与想象力。因此,软件开发从来都不是在一条永不停歇的流水线上不停地复制粘贴,它需要匠心的锤炼。
代码腐烂的味道
熵是一个很有趣的概念。房间中的灰尘没有被扫去,更多的灰尘将会出现在房间中;墙上有一些涂鸦没有被清除,更多的涂鸦将会出现在墙上;窗户破了没有去修好,更多的破坏者将会到来;程序中的糟糕代码没有被消灭,更多糟糕的代码将会出现在程序之中。
我特别喜欢 GitHub 为每个代码仓库所绘制的那幅“心率图”(过去一年里仓库的活动情况)。当我们看一看 OpenSSL、Bitcoin、Ethereum、Electron 这些优秀项目的“心率图”时,我们会发现它们的心率跳动是那么有力,那么优美!
在程序的世界里,代码的熵增实际上是非常可怕,甚至是毁灭性的!
如果我们身处于软件开发行业,总会听到甚至见到这样类似的场景。在一个缺乏有效管理的软件项目中,当无数的人为代码缝补上了各种颜色的补丁后,代码的混乱度将会不断增加,团队的效率将随之下降——他们不得不在这汪死水中缓慢前行,并时时提防脚下的陷阱。
当混乱度到达某一个值时,量变产生质变,开发人人员想要增加一个新功能,添加一个新特性,对性能进行一点提升,已经变成了沉重的负担。为了保持项目的进度,管理者会将更多的人手投入到这个项目。然而,新人想要弄懂原有的设计需要花费一些时间,新人想要在混乱的代码中明白代码的设计意图需要花费更多的时间。
重构?为时晚矣!项目的原有代码实在是太过复杂,太难以调试,开发人员在这样的项目上继续进行开发所需的工作量甚至超过了重写一个新项目所需的工作量。当团队中的每个人都承担着提高效率的压力而无法有效地降低这种混乱度时,更多的混乱往往会随之产生。在这种情况下,原有的项目将会被无情地抛弃,另起炉灶将是这支开发团队的唯一选择。
你闻过代码腐烂的味道吗?我当面对那样一滩绝望的死水之时,也只能一声长叹,然后留下一个丑陋的补丁。甚至,就像 Python 之禅中的这个反例一样,我丝毫不敢改动其中的一个字符。
1d = {}
2for c in (65, 97):
3 for i in range(26):
4 d[chr(i+c)] = chr((i+13) % 26 + c)
5
6print("".join([d.get(c, c) for c in s]))
代码腐烂的味道有哪些呢?《重构》列举了 24 中代码的坏味道。限于篇幅,我将仅仅列举其中的三种简略的谈一谈。
神秘的命名。代码不光会被机器所阅读,还会被我们所阅读。更为重要的是,这个读者很可能就是我们自己。当我们在阅读代码时看见一些神秘的命名,很难说这回给我们带来一种良好的阅读体验。毕竟,我们不愿意像福尔摩斯一般,在浩如烟海的代码中搜寻这些名字背后的故事。这种明确性,甚至被 Python 写入了设计哲学之中,即:“明确优于隐晦”。
1>>> import this
2The Zen of Python, by Tim Peters
3
4Beautiful is better than ugly.
5Explicit is better than implicit.
6Simple is better than complex.
7Complex is better than complicated.
8Flat is better than nested.
9Sparse is better than dense.
10Readability counts.
11Special cases aren't special enough to break the rules.
12Although practicality beats purity.
13Errors should never pass silently.
14Unless explicitly silenced.
15In the face of ambiguity, refuse the temptation to guess.
16There should be one-- and preferably only one --obvious way to do it.
17Although that way may not be obvious at first unless you're Dutch.
18Now is better than never.
19Although never is often better than *right* now.
20If the implementation is hard to explain, it's a bad idea.
21If the implementation is easy to explain, it may be a good idea.
22Namespaces are one honking great idea -- let's do more of those!
重复性代码。如果我们在不同的地方看到了相同的代码,设法将他们合并无疑是一种好的选择。为什么我们需要面向对象编程?其中的一个目的是,我们需要复用那些可重用的代码。在以太坊中,有一个名为 Ethereum Utils 的项目,专门用于为以太坊中的其他项目提供那些共同的工具类函数。这样一来,Bug 的修复回归到了单点上,重复性工作也被拒之门外。其他开发者无需进行复制粘贴,只需引入工具库即可。同时,当我们的项目变得十分庞大时,复用那些经过各种测试的代码总比使用一些新编写的代码要好得多。一来我们可以节省时间,二来我们可以避免引入一些前人已经犯过的错误——千万别小看墨菲定律。
全局数据。实际上,全局数据不仅仅是全局变量,还包括了类变量和单例。对于全局数据,如果我们能够保证在程序启动后,它们就是不可变的,不能再被修改,那这样还算安全。问题在于,可变的全局数据为我们的项目带来了极大的不确定性——在任何一个角落它们都有可能会被修改。全局数据就像盐一样,适量的盐为整道菜注入了灵魂,过多的盐将会毁了整道菜!
如何消除代码的坏味道,让代码永葆生机?《重构》一书用了 11 章的篇幅,从示例、原则、测试、重构手段等方面出发,细致入微地给我们讲解了帮助我们我们优雅地完成重构的必备技能。当然,现在也已经有很多团队在自动代码 review 方面为我们提供了一些高效的工具,能够根据一些预先建立的规则,帮助我们发现代码中的坏味道,并给我们提供消除坏味道的建议,例如 Codacy、codebeat、Codecov、Code Climate。
测试!测试!
软件开发不只是编写代码,更多的工作是在进行构建与测试,这是一项充满了未知与风险的工作。
重构的基础是测试集。一个好的项目,应该是有一套好的测试集的,是可以随时随地去折腾的,折腾完了,测试通过了,心里也就有了底。当然,怎么样衡量测试的好坏,似乎也没有什么标准。当你用测试覆盖率衡量测试集的好坏时,可能会陷入为了提高测试覆盖率而编写测试代码的覆盖率陷进之中。但无论如何,测试是软件开发中的一道防护网。
测试驱动开发(Test-Driven Development, TDD)起源于极限编程(Extreme Programming),它依赖于一个标志性的短循环:编写能够得到失败结果的测试代码,编写代码使测试通过,进行重构保证代码整洁。在整个循环中,测试山羊(起源于 PyCon 2010,是 Python 测试社区的 TDD 非官方吉祥物)起到了向导的作用,它机敏地指引着我们朝着正确的方向快步前进。
站在工具的肩膀上
比起早期的开发环境,现代开发环境已经有了显著的变化,有许多自动化重构工具可以帮助我们完成一些简单的重构工作。例如,在 JetBrains 的全家桶中,我们只需要点击 Refactor 菜单中的对应选项,或者使用快捷键,就可以利用集成在其中的重构工具帮我们完成这些工作。从这个角度来看,在 IDE 的菜单中单独列出了 Refactor 菜单,足以见得重构在软件开发中的地位。
实际上,想要实现一个优秀的重构工具,是一件非常具有挑战性的工作,因为这样的工具不仅要能够理解和修改语法树,还要能把修改后的代码准确填回到编辑器视图之中。因此,虽然通常工具完成的重构是可靠,可有的时候我们心里还是会没底。比如当我们使用 PyCharm 对一段 Python 代码进行重构时,“动态类型一时爽,代码重构火葬场”这样的至理名言便时常出现在我们的耳边。这个时候,如果我们用 Travis CI、Circle CI、Jenkins 这样的工具去跑一跑我们的测试集,当工具给我们修改后的代码一个明确的 ✔️,我们的心里也便有了底。
重构不止
《重构》是一本被广泛称赞的经典技术图书。如今,重构的概念早已变得愈来愈宽泛,代码可以重构,系统可以重构,组织架构也可以重构,“敏捷”与“精益”的风潮也早已风靡于各领域。距离《重构》第一版中译本出版已经过去十多年了,今年,《重构》第二版中译本已经正式发行。重构在软件开发领域的重要性已经具有了广泛的共识。因此,我们可以说,《重构》是每一名优秀的软件开发从业者都应该读一读的枕边书。
《重构:改善既有代码的设计(第2版)》
作者:[美]马丁·福勒(Martin Fowler)
译者:熊节 林从羽
重构,一言以蔽之,就是在不改变外部行为的前提下,有条不紊地改善代码。20多年前,正是《重构:改善既有代码的设计》第1版的出版,使重构终于从编程高手们的小圈子走出,成为众多普通程序员日常开发工作中不可或缺的一部分。如今,Martin Fowler的《重构:改善既有代码的设计》一书已经成为全球有经验的程序员手中的利器,既可用来改善既有代码的设计、提升软件的可维护性,又可用于使既有代码更易理解、焕发出新的活力。
这本备受关注的第2版在第1版的基础上做了全面修订,反映了编程领域业已发生的许多变化。书中给出了60多个可行的重构,每个重构都介绍了经过验证的代码变换手法的动机和技术。第2版中介绍的重构列表更加内聚,并用JavaScript语言重写了代码范例。此外,第2版中还新增了与函数式编程相关的重构范例,旨在教会读者如何在没有类的环境下开展重构。本书提出的重构准则将帮助开发人员小步地修改代码,从而减少了开发过程中的风险。
头条文章最新推出了每日一题的互动活动,
你敢不敢来挑战,赢取优秀书籍1本,
更多精彩
在公众号后台对话框输入以下关键词
查看更多优质内容!
女朋友 | 大数据 | 运维 | 书单 | 算法
大数据 | JavaScript | Python | 黑客
AI | 人工智能 | 5G | 区块链
机器学习 | 数学 | 留言送书