摘要: 每一个程序员都应该读的一本书。

  • 原文:重构:一项常常被忽略的基本功
  • 作者:hengg

Fundebug经授权转载,版权归原作者所有。

五月初的时候朋友和我说《重构》出第 2 版了,我才兴冲冲地下单,花了一个礼拜时间一口气把它读完后,才有了这篇书评。掩卷沉思,我无比赞同豆瓣网友“天心一”的评论:

这本书虽然很流行,但是应该看它而没有看的人,还是太多太多了。

一个老读者的自白

作为一个开发者,2012年初识本书的时候,我在写 Java;2019年本书再版,我在写 JavaScript。真是应了那句老话儿:“凡是可以用 JavaScript 来写的应用,最终都会用 JavaScript 来写。”

JavaScript 特别适合重构,因为它很容易写的无法维护。

当然这只是个玩笑,实际上作者也解释过:重构背后的理念和架构适用于任何编程语言,选择 JavaScript 只是因为它应用的比较广泛。无论使用哪种编程语言都可以写出优秀的或者糟糕的代码,同样也都可以以本书的思路和技巧进行重构。

使用 JavaScript 展示代码范例,并不意味这本书中介绍的技巧只适用于JavaScript。

对比新旧两版,作者“重构”了这本书:前几章有所扩展,后几章结构调整较大,移除了原来的 12-14 章。总的来说,重构后的第 2 版更接地气、更适应时代:不再有“大型重构”,更多地聚焦操作的细节。

“Fowler 先生不仅没有拔高,反而把功夫做得更扎实了。” —— 摘自译者序

虽然本书的副标题是“改善既有代码的设计”,但通读全书之后,我觉得这本书对于设计新系统时如何避免“坏味道”也是很有指导意义的。

重构和敏捷开发是一对亲兄弟

提重构就不能不提敏捷开发,马丁·福勒本身就是敏捷开发的发起者之一。敏捷作为“当红炸子鸡”,与重构有着很多相似的地方。

一是,这两者都容易成为“挂羊头,卖狗肉”中的“羊头”,很多情况下,所谓的重构就是抽出时间来重写现有的几乎无法维护的代码,就如同很多“敏捷”只做到了“不拒绝需求变更”而没有真正做到响应变化;二是,它们实现起来都是一定难度且它们的实践过程可以是交叉的——它们都着眼于具体细节而不是空架子,都欢迎变化,都强调小步快走、持续改进;三是,敏捷开发很重要的两个环节就是设计与重构,两者相辅相成,彼此互补,在实践的过程中保持较强的适应力。

重构的技巧

可以说,我在重构过程中遇到的问题大多都能在本书中找到答案。

我们看看作者对重构的定义:

重构(名词): 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

重构(动词): 使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

为何重构、如何重构、重构的原则与手法,都可以在这本书中找到。从第 5 章起作者提供了多达 300 页的重构名录、60 余项重构的具体技巧(老版本是 70 多项,新版本移除了大规模项目的重构)。我觉得这一份非常详尽的重构手法清单更接近于字典,适合粗读之后在用到的时候再具体查阅。

至于什么时候能够用到这份名录,作者在第 3 章也有介绍:当代码有了“坏味道”就可以着手进行重构了。所谓“坏味道”,我认为并非是一程不变的准则,而是需要根据团队、项目、采用的技术栈等各方面综合得出的一种无法定量描述的经验。所以,作者用了“味道”这样一种体验来代指需要重构的地方。在作者列出的每种“坏味道”中,都给出了对应的重构手法。虽然作者罗列的 20 多种“坏味道”覆盖面很广,但是你和你的团队仍然可以总结出自己的经验来指导重构。实际上,与第 1 版相比,第 2 版中的“坏味道”增加了“神秘命名”“全局数据”“循环语句”,删除了“不完美的库类”。

我认为本书最重要也最容易被忽略的章节就是第 4 章——构筑测试体系。在第 4 章中,作者通过一个生产计划的示例一步一步的构建了一个完整的单元测试体系。显然,掌握单元测试是有一定成本的,这就导致有些开发者(尤其是前端领域)完全不注重单元测试。他们认为测试是QA的职责,自己只需要保证冒烟测试通过即可。然而反直觉的是,良好的单元测试不但是重构的先决条件和好帮手,而且能帮我们整理设计的思路,从而更好的写出优秀的代码。因为在写单元测试的时候,我们会假设自己是一个“代码破坏者”,思考如何破坏代码的运行、寻找那些可能出错的边界条件。单元测试的编写和运行可以在写完代码后进行,也可以在写代码之前动手。先写单元测试再写代码的技巧叫作测试驱动开发(TDD),也是敏捷开发的基石之一。关于TDD的技艺,作者的好友 Kent Beck 专门写了一本书,即《测试驱动开发》。

作者在第 1 章的示例中提到:“小步快走,代码永远处于可工作状态。”而且作者特意强调:“每当我要进行重构的时候,第一个步骤永远相同:我得确保即将修改的代码拥有一组可靠的测试。”

对于单元测试,我有一点小小的心得可以与大家分享:**尽量编写纯函数。**纯函数是没有副作用的函数,给出同样的参数值,纯函数总是返回同样的结果,它不依赖于参数以外的值。显然,纯函数更便于单元测试。

当然单元测试也不是万能的,它不可能检出所有的bug,而且单元测试集的覆盖率也是一个见仁见智的指标,具体需要写多少单元测试,覆盖多少代码,都是需要我们在开发中结合实际情况自己权衡的。无论如何,单元测试一直是一中非常重要却常常被忽视的技能。

另外,我在开发实践中坚持一个“432”的原则,供大家参考:

  • 一个类包括注释代码不要超过400行;
  • 一个纯函数最好不要超过30行;
  • 函数内循环嵌套最多2层。

重构的现状

有些朋友对“重构”是不支持甚至是深恶痛绝的。

  • 一部分开发者不愿意把精力“浪费”在重构上

他们觉得重构是“给飞行中的飞机修引擎”,有可能出现很多问题却带不来多少拿得出手的成绩;重构总是会在“不经意间”破坏原有功能,带来的麻烦很多,投入与收益完全不成比例,也很少会是面试的重点,花精力在这上面实在是费力不讨好。

  • 许多leader反对盲目重构

在创业公司里基本不会有重构的呼声,原因无须赘言;而在一些大企业里,leader们也不是都喜欢重构,因为花时间重构意味着占用了开发新功能的时间,在代码还能跑起来甚至看起来跑得还不错的时候去重构无疑是画蛇添足;与重构带来的风险相比,重构带来的好处就不是那么有说服力了。

  • 大部分QA对重构持谨慎的质疑态度

代码的变动意味着需要进行回归测试,而敏捷当道的时代,每个迭代中QA的关注重点都在新功能上,能够分配给回归测试的精力很有限,而在测试通过后的重构极有可能导致此次变更对QA不透明,无形中增加了上线的风险。

我认为以上几种反对重构的场景都是不恰当的重构导致的。

大家只是越来越接纳“重构”这个词,因为这个词听起来很好,有一种积极应对变化的感觉,但真正在做的还是跟以前一样,毫无规矩的修改。

在实践中,重构的要求是很高的:它需要有足够详尽的单元测试,需要有持续集成的环境,需要随时随地在“小步伐地永远让代码处于可工作状态”下去进行改善。正是因为许多项目的“重构”是在并不满足以上条件也没有经过成本估算、策略规划的情况下进行的,自然很容易导致失败。

  • 水土不服

实际上,还有一部分开发者虽然认识到了重构是提升代码质量的有效手段,是诸如“在当下努力工作,以免日后有更多的活儿”此类观念的具现。然而在某种程度上说,这在当前996.icu大环境下是不适用的。关于这一点就只能见仁见智、自己衡量了。

没有银弹

最后,我想说一句: 没有银弹

重构和设计模式一样,是对于最佳实践的提炼,是一系列技巧的集合,它不是打通任督二脉的灵丹妙药。如果你是一个有追求但却从来没有系统地了解过重构的程序员(当然我不相信世界上会有这种程序员),那你会发现,你在日常工作中不经意间已经用过了这本书中提到的各种重构手法。

重构是注重实践的技艺,仅仅了解其理念而忽视实践则有如抟沙作饭,白费心思;而企图把它当做“万金油”来解决所有问题也只会陷入不恰当重构的陷阱,最终得不偿失。只有在合适的场景下恰当的实践,才会实现其应有的价值。

转载于:https://juejin.im/post/5d044b0d6fb9a07ef06f9138

重构:一项常常被忽略的基本功相关推荐

  1. 物料清单的重构———虚项

    天津大学 周玉清 物料清单是正规计划系统的基础.出于计划的目的,企业的物料清单有时必须重构.应 当强调的是,不是由于MRPⅡ为企业带来了重构物料清单的工作负担,而是由于MRPⅡ为企业 重构物料清单的客 ...

  2. 返回值被忽略_聊聊如今智能手机中一项容易被忽略的参数信息--闭环马达

    文|破晓时刻的等待 来源|电子产品闲聊站公众号(ID:dzcpxlz) 现在的各家手机厂商都喜欢在发布会中吹嘘自家的智能手机堆料多么的足,可实际情况是总有一些配置受限与成本或者是供应链而在消费者不起眼 ...

  3. android提交项目到svn,Android Studio 配置SVN,通过Share project提交项目和实现忽略文件...

    1:首先电脑本身装的SVN必须在bin目录下包含svn.exe文件,如下图. 这个文件是在安装SVN时选择安装command line client tools才会出现.如果没有,需要重新安装SVN. ...

  4. ccf命令行选项只能用c实现_CCF-201403-3-命令行选项

    问题描述 试题编号: 201403-3 试题名称: 命令行选项 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项.每 ...

  5. SVN设置忽略文件和文件夹

    文章目录 忽略 **未版本控制的文件** 忽略 **已版本控制的文件** 使用模式匹配配置忽略 设置全局忽略 设置项目级忽略策略 设置目录级忽略策略 结论 被一个小小的设置征服的恐惧,遂花了一把时间, ...

  6. 研发团队资源成本优化实践

    背景 工程师主要面对的是技术挑战,更关注技术层面的目标.研发团队的管理者则会把实现项目成果和业务需求作为核心目标.实际项目中,研发团队所需资源(比如物理机器.内存.硬盘.网络带宽等)的成本,很容易被忽 ...

  7. 研发团队资源成本优化实践 1

    背景 工程师主要面对的是技术挑战,更关注技术层面的目标.研发团队的管理者则会把实现项目成果和业务需求作为核心目标.实际项目中,研发团队所需资源(比如物理机器.内存.硬盘.网络带宽等)的成本,很容易被忽 ...

  8. 北京学习linux,北京linux学习

    课程详情 培训目标: 本课程涵盖代码重构(2天),设计重构(1天),架构重构(1天)3个层次. 主要包含以下几个方面的内容: n 结合电信.金融等多个项目案例进行分析.本次培训所用案例均为真实项目案例 ...

  9. 《Scrum精髓—敏捷转型指南》读后感

    首先很庆幸,能在适合的时间,遇到了这样一本适合的书.之所以这样说,是因为在遇到这本书前,我还是一名单纯的程序员,"增删改查"的业务代码,占据了我大多数的时间,本就繁杂的工作,还被一 ...

最新文章

  1. mysql like 命中索引
  2. java flexpaper_java web word文件 pdf文件在线预览源码(flexpaper)
  3. 【Linux 内核】编译 Linux 内核 ① ( 下载指定版本的 Linux 内核源码 | Linux 内核版本号含义 | 主版本号 | 次版本号 | 小版本号 | 稳定版本 )
  4. 2022年改变数据中心行业的八大趋势
  5. PP: 混合生产方式(MTO与MTS为例)
  6. (双指针、二分Binary Search) leetcode 658. Find K closest Elements
  7. 跟多导出数据库的方法
  8. python绘制折线图保存_python如何画折线图
  9. Bailian3708 1的个数【进制】
  10. 同一个ip服务器comcat下部署第二个项目怎么设置,用nginx在同一服务器端口下部署多个项目,第二个项目打开后页面空白?...
  11. xShell+xFtp 破解版
  12. 基于EEG信号的情绪分析数据库DEAP论文调研
  13. 全国省市区县json数据
  14. 日文翻译器支持整篇文档批量翻译
  15. 笔记本单/双网卡如何同时上内外网
  16. 东芝抢先一步,推出了全球首款16TB容量的硬盘MG08系列
  17. redis-trib.rb 使用详解
  18. Terraform操作阿里云实例
  19. 用于视力恢复的脑机接口综述(一)
  20. 一文搞懂候选码、主码、全码、外码、主属性、主键、主关键字、非主属性清晰总结

热门文章

  1. linux下模拟CPU占用100%小程序
  2. 穷举n位二进制数 (深搜、回溯_子集树)
  3. jsp定义一个变量在html,jsp中变量及方法的声明与使用说明
  4. 按键的c语言代码表,各种按键模式的扫描
  5. linux memcache 源码包,Linux 安装Memcache扩展支持(示例代码)
  6. Xamarin Essentials教程屏幕常亮ScreenLock
  7. Xamarin Essentials教程屏幕状态DeviceDisplay
  8. Xamarin 2017.9.19更新
  9. Xamarin.Forms特殊的视图BoxView
  10. java 正则 捕获_Java通过正则表达式捕获组中的文本