(文末福利)如果代码莫名其妙跑起来了,就不要去动它了……吗?

反射冰川
• 阅读 3059

🤫 关注 Zilliz 微信公众号并回复「重构」🤫
获取《重构:改善既有代码的设计》超详细思维导图


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

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(提炼函数)

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

 void printOwing(double amount) {
     printBanner();
     //print details
     System.out.println ("name:" + _name);
     System.out.println ("amount" + amount);
 }

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

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 将临时变量消灭掉。
  • 将被提炼码中需要读取的局部变量,当作参数传给目标函数
  • 处理完所有局部变量之后,进行编译
  • 在源函数中,将被提炼码替换为「对目标函数的调用」
  • 如果你将任何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼码的外围。如果是,现在你可以删除这些声明式了
  • 编译,测试

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

更多资源

这本书第 5 - 12 章是一份「重构列表」,有些概念比较抽象。为了更好地理解列表的内容,小编推荐 Refactoring Guru 这个网站,网站上不仅有许多关于重构的实际解释,还介绍了常用的设计模式和设计原则。这个网站可以帮助你用简单便捷的方式迅速掌握重构的理念、学习各个设计模式。

(文末福利)如果代码莫名其妙跑起来了,就不要去动它了……吗?

想要了解更多详细内容?关注 Zilliz 微信公众号并回复书名「重构」

即可获得小编整理的《重构:改善既有代码的设计》高清思维导图

积累代码量很重要,

读书、读好书也很重要。

「Zilliz 好书推荐」栏目,

旨在与你分享技术成长相关的书籍,

与你一起先把书读厚,再把书读薄。

——————————————————————————

Zilliz 以重新定义数据科学为愿景,致力于打造一家全球领先的开源技术创新公司,并通过开源和云原生解决方案为企业解锁非结构化数据的隐藏价值。

Zilliz 构建了 Milvus 向量数据库,以加快下一代数据平台的发展。Milvus 数据库是 LF AI & Data 基金会的毕业项目,能够管理大量非结构化数据集,在新药发现、推荐系统、聊天机器人等方面具有广泛的应用。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这