前言
“重构”这个词对于大部分工程师来说都不生疏。实际上大部分人都只是“听得多做得少”,真正进行过代码重构的人不多,而把继续重构作为开发的一部分的人,就更是少之又少了。
一方面,重构代码对一个工程师能力的要求,要比单纯写代码高得多。重构须要你能洞察出代码存在的坏滋味或者设计上的有余,并且能正当、熟练地利用设计思维、准则、模式、编程标准等理论知识解决这些问题。
另一方面,很多工程师对为什么要重构、到底重构什么、什么时候重构、又该如何重构等相干问题了解不深,对重构没有系统性、全局性的意识,面对一堆烂代码,没有重构技巧的领导,只能想到哪改到哪,并不能全面地改善代码品质。
重构次要蕴含以下几个方面:
对重构概括性的介绍,包含重构的目标(why)、对象(what)、机会(when)、办法(how);
保障重构不出错的伎俩,重点解说单元测试和代码的可测试性;
不同规模的重构,重点解说大规模高层次重构(比方零碎、模块、代码构造、类与类之间的交互等的重构)和小规模低层次重构(类、函数、变量等的重构)。
重构的目标:为什么要重构(why)?
巨匠是怎么形容重构的。软件设计巨匠 Martin Fowler 是这样定义重构的:“重构是一种对软件内部结构的改善,目标是在不扭转软件的可见行为的状况下,使其更易了解,批改老本更低。”
实际上,当讲到重构的时候,很多书籍都会援用这个定义。这个定义中有一个值得强调的点:“重构不扭转内部的可见行为”。咱们能够把重构了解为,在放弃性能不变的前提下,利用设计思维、准则、模式、编程标准等实践来优化代码,批改设计上的有余,进步代码品质。
简略理解重构的定义之后,咱们重点来看一下,为什么要进行代码重构?
首先,重构是时刻保障代码品质的一个极其无效的伎俩,不至于让代码腐化到无可救药的境地。我的项目在演进,代码不停地在堆砌。如果没有人为代码的品质负责任,代码总是会往越来越凌乱的方向演进。当凌乱到肯定水平之后,质变引起量变,我的项目的保护老本曾经高过从新开发一套新代码的老本,想要再去重构,曾经没有人能做到了。
其次,优良的代码或架构不是一开始就能齐全设计好的,就像优良的公司和产品也都是迭代进去的。咱们无奈 100% 遇见将来的需要,也没有足够的精力、工夫、资源为边远的将来买单,所以,随着零碎的演进,重构代码也是不可避免的。
最初,重构是防止适度设计的无效伎俩。在咱们保护代码的过程中,真正遇到问题的时候,再对代码进行重构,能无效防止后期投入太多工夫做适度的设计,做到对症下药。
除此之外,重构对一个工程师自身技术的成长也有重要的意义。
从重构的定义来看,重构实际上是对咱们学习的经典设计思维、设计准则、设计模式、编程标准的一种利用。重构实际上就是将这些理论知识,利用到实际的一个很好的场景,可能锤炼咱们纯熟应用这些理论知识的能力。除此之外,平时堆砌业务逻辑,你可能总感觉没啥成长,而将一个比拟烂的代码重形成一个比拟好的代码,会让你很有成就感。
除此之外,重构能力也是掂量一个工程师代码能力的无效伎俩。所谓“高级工程师在保护代码,高级工程师在设计代码,资深工程师在重构代码”,这句话的意思是说,高级工程师在已有代码框架下批改 bug、批改增加性能代码;高级工程师从零开始设计代码构造、搭建代码框架;而资深工程师为代码品质负责,须要察觉代码存在的问题,重构代码,时刻保障代码品质处于一个可控的状态。
重构的对象:到底重构什么(what)?
依据重构的规模,咱们能够抽象地分为大规模高层次重构(以下简称为“大型重构”)和小规模低层次的重构(以下简称为“小型重构”)。
大型重构指的是对顶层代码设计的重构,包含:零碎、模块、代码构造、类与类之间的关系等的重构,重构的伎俩有:分层、模块化、解耦、形象可复用组件等等。这类重构的工具就是咱们学习过的那些设计思维、准则和模式。这类重构波及的代码改变会比拟多,影响面会比拟大,所以难度也较大,耗时会比拟长,引入 bug 的危险也会绝对比拟大。
小型重构指的是对代码细节的重构,次要是针对类、函数、变量等代码级别的重构,比方标准命名、标准正文、打消超大类或函数、提取反复代码等等。小型重构更多的是利用咱们能前面要讲到的编码标准。这类重构要批改的中央比拟集中,比较简单,可操作性较强,耗时会比拟短,引入 bug 的危险相对来说也会比拟小。你只须要熟练掌握各种编码标准,就能够做到得心应手。
重构的机会:什么时候重构(when)?
搞清楚了为什么重构,到底重构什么,咱们再来看一下,什么时候重构?是代码烂到肯定水平之后才去重构吗?当然不是。因为当代码真的烂到呈现“开发效率低,招了很多人,天天加班,出活却不多,线上 bug 频发,领导发飙,中层大刀阔斧,工程师埋怨一直,查找 bug 艰难”的时候,基本上重构也无奈解决问题了。
我集体比拟拥护,平时不重视代码品质,堆砌烂代码,切实保护不了了就大刀阔斧地重构、甚至重写的行为。有时候我的项目代码太多了,重构很难做得彻底,最初又搞进去一个“四不像的怪物”,这就更麻烦了!所以,寄希望于在代码烂到肯定水平之后,集中重构解决所有问题是不事实的,咱们必须摸索一条可继续、可演进的形式。
所以,我特地提倡的重构策略是继续重构。平时没有事件的时候,你能够看看我的项目中有哪些写得不够好的、能够优化的代码,被动去重构一下。或者,在批改、增加某个性能代码的时候,你也能够棘手把不合乎编码标准、不好的设计重构一下。总之,就像把单元测试、Code Review 作为开发的一部分,咱们如果能把继续重构也作为开发的一部分,成为一种开发习惯,对我的项目、对本人都会很有益处。
只管咱们说重构能力很重要,但继续重构意识更重要。咱们要正确地对待代码品质和重构这件事件。技术在更新、需要在变动、人员在流动,代码品质总会在降落,代码总会存在不完满,重构就会继续在进行。时刻具备继续重构意识,能力防止开发初期就适度设计,防止代码保护的过程中品质的降落。而那些看到他人代码有点瑕疵就一顿乱骂,或者花尽心理去构思一个完满设计的人,往往都是因为没有建立正确的代码质量观,没有继续重构意识。
重构的办法:又该如何重构(how)?
后面咱们讲到,依照重构的规模,重构能够抽象地分为大型重构和小型重构。对于这两种不同规模的重构,咱们要区别对待。
对于大型重构来说,因为波及的模块、代码会比拟多,如果我的项目代码品质又比拟差,耦合比较严重,往往会牵一发而动全身,原本感觉一天就能实现的重构,你会发现越改越多、越改越乱,没一两个礼拜都搞不定。而新的业务开发又与重构相冲突,最初只能大功告成,revert 掉所有的改变,很失落地又去堆砌烂代码了。
在进行大型重构的时候,咱们要提前做好欠缺的重构打算,井井有条地分阶段来进行。每个阶段实现一小部分代码的重构,而后提交、测试、运行,发现没有问题之后,再持续进行下一阶段的重构,保障代码仓库中的代码始终处于可运行、逻辑正确的状态。每个阶段,咱们都要管制好重构影响到的代码范畴,思考好如何兼容老的代码逻辑,必要的时候还须要写一些兼容过渡代码。只有这样,咱们能力让每一阶段的重构都不至于耗时太长(最好一天就能实现),不至于与新的性能开发相冲突。
大规模高层次的重构肯定是有组织、有打算,并且十分审慎的,须要有教训、熟悉业务的资深共事来主导。而小规模低层次的重构,因为影响范畴小,改变耗时短,所以,只有你违心并且有工夫,随时都能够去做。实际上,除了人工去发现低层次的品质问题,咱们还能够借助很多成熟的动态代码剖析工具(比方 CheckStyle、FindBugs、PMD),来主动发现代码中的问题,而后针对性地进行重构优化。
对于重构这件事件,资深的工程师、我的项目 leader 要负起责任来,没事就重构一下代码,时刻保障代码品质处在一个良好的状态。否则,一旦呈现“破窗效应”,一个人往里堆了一些烂代码,之后就会有更多的人往里堆更烂的代码。毕竟往我的项目里堆砌烂代码的老本太低了。不过,放弃代码品质最好的办法还是打造一种好的技术气氛,以此来驱动大家被动去关注代码品质,继续重构代码。
重点回顾
明天的解说让你对重构有个正确的、全局性的认知,建设继续重构意识。这可能比教会你一些重构技巧更重要,因为很多技术问题自身就不是单纯靠技术来解决的,更重要的是要有这种认知和意识。
1. 重构的目标:为什么重构(why)?
对于我的项目来言,重构能够放弃代码品质继续处于一个可控状态,不至于腐化到无可救药的境地。对于集体而言,重构十分锤炼一个人的代码能力,并且是一件十分有成就感的事件。它是咱们学习的经典设计思维、准则、模式、编程标准等理论知识的练兵场。
2. 重构的对象:重构什么(what)?
依照重构的规模,咱们能够将重构大抵分为大规模高层次的重构和小规模低层次的重构。大规模高层次重构包含对代码分层、模块化、解耦、梳理类之间的交互关系、形象复用组件等等。这部分工作利用的更多的是比拟形象、比拟顶层的设计思维、准则、模式。小规模低层次的重构包含标准命名、正文、修改函数参数过多、打消超大类、提取反复代码等等编程细节问题,次要是针对类、函数级别的重构。小规模低层次的重构更多的是利用编码标准这一理论知识。
3. 重构的机会:什么时候重构(when)?
我反复强调,咱们肯定要建设继续重构意识,把重构作为开发必不可少的局部,融入到日常开发中,而不是等到代码呈现很大问题的时候,再大刀阔斧地重构。
4. 重构的办法:如何重构(how)?
大规模高层次的重构难度比拟大,须要组织、有打算地进行,分阶段地小步快跑,时刻让代码处于一个可运行的状态。而小规模低层次的重构,因为影响范畴小,改变耗时短,所以,只有你违心并且有工夫,随时随地都能够去做。
一篇讲透 Java 重构之道