前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如果代码莫名其妙跑起来了,就不要去动它了……吗?

如果代码莫名其妙跑起来了,就不要去动它了……吗?

作者头像
Zilliz RDS
修改2021-12-01 21:02:19
1.2K0
修改2021-12-01 21:02:19
举报
文章被收录于专栏:Reinvent Data Science

虽然代码还是可以跑,但是各种规则越来越复杂、核心继承体系越来越凌乱、系统的维护工作越来越重……

1999 年,Martin Fowler 作为技术顾问造访了一个项目,他建议项目经理好好整理这些乱糟糟的代码。然而,项目经理表示:🙏算了吧🙏

六个月后,这个项目宣告失败,因为代码太复杂难以调试,性能也达不到要求。

这件事给 Martin 留下很深的印象,随后,他写下了《重构:改善既有代码的设计》

《重构》出版 22 年后,已成为软件开发领域不可替代的经典。这本书解释了重构的原理和最佳实践方式,并给出了修改代码的动机和具体案例,值得反复消化咀嚼。

这本书还凝聚了多位软件开发领域专家的宝贵经验:摩根大通架构师 Bill Opdyke 在第 13 章记述他将重构技术应用到商业开发过程中的一些问题;软件开发方法学泰斗 Kent Beck 和 Don Roberts 合写了第 14 章,展望重构技术的未来——自动化工具;Kent Beck 还写了最后一章,总结如何学习重构。

你将从这本书中获得:

  • 理解什么是重构、为什么要重构、何时重构,理解
  • 理解重构原则:一次一小步地修改代码并多次测试
  • 实操演练重构的动机和方法,使既有代码更易理解、提升软件的可维护性

无论你是软件工程师还是产品经理,都需要翻一翻这本经典;而系统设计师和架构师则更有必要了解重构原理,根据需要在自己的项目中运用重构技术、优化系统性能。

💡 小编提醒,这本书中第一版的案例语言使用 Java,第二版的语言使用 JavaScript。总体而言,作者展示的重构手法在各种主流的面向对象语言中基本上都可以通用。

为何重构?

第二章中,作者详细介绍了重构的价值。重构不仅可以改进软件设计本身的缺陷、帮助找到 bug、提升开发速度,还可以使软件更容易被理解——这是因为,程序设计很大程度上是人与计算机、人与人的沟通。

Martin Fowler 曾提及,任何一个傻瓜都能写出计算机可以理解的代码,唯有能写出人类容易理解的代码的,才是优秀的程序员。

所谓程序设计,便是与计算机交谈。你编写代码告诉计算机做什么事情,它的响应则是按照你的指示行动。你得及时填补「想要它做什么」和「告诉它做什么」之间的缝隙。这种编程模式的核心就是「准确说出我想要的」。除了计算机之外,你的源码还有其他读者。计算机是否多花了几个小时来编译,又有什么关系呢?如果一个程序员花费一周时间来修改某段代码,那才要命呢——如果他理解了你的代码,这个修改原本只需一小时。……而很多时候,那个未来的程序员就是我自己。

《重构(第2版)》译者熊节也曾谈到,「编程其实是个社会活动」

一方面,程序员要把自然语言说出来的需求翻译成机器能运行的机器语言;另一方面,翻译出来的结果(也就是代码)还要支撑团队(包括技术和非技术的团队)不断地在它基础上协作和交流。……编程的大挑战不是把代码写出来,而是要在代码的基础上建立有效的多方沟通。

那么,我们何时需要重构?书中第三章列举了一些「代码的坏气味」。「坏气味」指的是代码中某些不完美之处,开发人员可以通过这些细节上的征兆在代码中追捕到更大问题。小编不禁联想到了《Clean Code》中的「好气味」和「坏气味」

一个重构案例

众所周知,重构有风险,挖坑需谨慎。如果重构方式不恰当,风险反而更大。

试想一下这样的情况:你挖掘自己的代码,很快就发现了一些可以修改的地方,于是你挖得更深。挖得愈深,可以修改的地方就愈多……最后,你给自己挖了一个大坑,再也爬不出去了。

为了避免掉进坑里,重构必须按照一定的原则和方法进行。

作者在第 5 - 12 章给出了一个重构列表,每一个重构案例都写明了重构适用的情景、动机、重构方法。让我们来看一个案例吧:

Extract Method(提炼函数)

你有一段代码可以被组织在一起并独立出来:

代码语言:javascript
复制
 void printOwing(double amount) {
     printBanner();
     //print details
     System.out.println ("name:" + _name);
     System.out.println ("amount" + amount);
 }

将这段代码放进一个独立函数中,并让函数名称解释该函数的用途:

代码语言:javascript
复制
void printOwing(double amount) {
     printBanner();
     printDetails(amount);
 }
 void printDetails (double amount) {
     System.out.println ("name:" + _name);
     System.out.println ("amount" + amount);
 }

这样做的动机:

函数应该简短而有的良好命名。原因如下:

  • 首先,如果每个函数的粒度都很小,那么函数之间彼此复用的机会就更大;其次,这会使高层函数码读起来就像一系列注释;再者,如果函数都是细粒度,那么函数的覆写也会更容易些。
  • 如果提炼动作可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码还长也无所谓。

重构方法

  • 创造一个新函数,以它「做什么」来命名, 而不是以它「怎样做」命名
  • 将提炼出的代码从源函数拷贝到新建的目标函数中
  • 仔细检查提炼出的代码,看看其中是否引用了「作用域限于源函数」的变量(包括局部变量和源函数参数)
  • 检查是否有「仅用于被提炼码」的临时变量,如果有,则在目标函数中将它们声明为临时变量
  • 检查被提炼码,看看是否有任何局部变量的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼码处理为一个查询,并将结果赋值给相关变量。如果很难这样做,或如果被修改的变量不止一个,你就不能仅仅将这段代码原封不动地提炼出来。你可能需要先使用 Split Temporary Variable 方法,然后再尝试提炼。也可以使用 Replace Temp with Query 将临时变量消灭掉。
  • 将被提炼码中需要读取的局部变量,当作参数传给目标函数
  • 处理完所有局部变量之后,进行编译
  • 在源函数中,将被提炼码替换为「对目标函数的调用」
  • 如果你将任何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼码的外围。如果是,现在你可以删除这些声明式了
  • 编译,测试

随后,作者给出了无局部变量、有局部变量、对局部变量再赋值三种范例,手把手解释如何提炼函数。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ZILLIZ 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档