代码重构是每个程序员日常工作中必不可少的一部分,从刚入行的时候听前辈说要重构,到自己真正动手重构,再到这三年来不断地在项目中实践,我对代码重构的理解发生了很大的变化。一开始我以为重构就是把代码写得更漂亮,后来发现不是那么简单,重构是一门学问,有很多技巧和原则,也有很多坑。今天就来分享一下我用了三年代码重构技巧之后,总结出来的一些道理和经验。
一、重构不是重写,这是最容易犯的错
刚入行的时候,我对重构的理解就是"把烂代码重新写一遍",所以每次接到重构任务,我就兴冲冲地把旧代码全部删掉,然后按照自己的想法重新写一套。结果呢?经常是写了一半发现不对,旧代码里有很多业务逻辑和边界情况我没考虑到,新写的代码bug一堆,最后还得把旧代码翻出来对照着改,费时费力,还容易出问题。
后来我才明白,重构和重写是两回事。重构是在不改变软件外部行为的前提下,对代码内部结构进行调整,让代码更容易理解和维护。重点是"不改变外部行为",也就是说,重构之后,功能应该和原来一模一样,用户感知不到任何变化。而重写是把原来的代码扔掉,重新写一套,这风险就大了,很容易引入新的bug。
所以正确的重构方式应该是小步快跑,每次只改一点点,改完就测试,确保没问题了再改下一点。而不是一上来就大动干戈,把整个模块都推翻重来。这三年我见过太多重构失败的案例,基本上都是因为把重构做成了重写,最后项目延期,bug满天飞,得不偿失。
二、重构之前一定要有测试,这是安全网
这是我踩过最大的坑之一。以前我重构代码,上来就改,改完自己手动点两下觉得没问题就提交了,结果上线之后出了各种问题,因为很多边界情况我没测到。后来我才明白,没有测试的重构就是在裸奔,你根本不知道自己改完之后有没有破坏原来的功能。
所以现在我重构之前,第一件事就是给要重构的代码补上单元测试和集成测试,确保原来的功能都有测试覆盖。然后我再开始重构,每改完一点就跑一遍测试,测试全过了说明没问题,继续改下一点。如果测试挂了,说明我改坏了,马上回退或者修复。
有了测试这个安全网,重构的时候心里就踏实多了,不用担心改坏了不知道。而且测试本身也是文档,后来的人看测试就知道这段代码是干什么的,有哪些边界情况。
当然,很多老项目根本没有测试,这时候怎么办?我的经验是,先从最核心、最容易出问题的地方开始补测试,不用追求100%覆盖率,先把关键路径覆盖了就行。然后重构的时候,改到哪里就把测试补到哪里,慢慢积累,时间长了覆盖率就上来了。
三、命名是重构的第一步,也是最重要的一步
很多人觉得重构就是要用各种设计模式,各种高级技巧,其实不是。我这三年的经验是,重构最有效、性价比最高的事情,就是给变量、函数、类起一个好名字。
我见过太多烂代码,根本原因就是命名太烂,变量叫a、b、c,函数叫doSomething、handleData,看了半天不知道这段代码在干什么。这种代码,你就算用了再高级的设计模式,别人还是看不懂,还是难维护。
所以我现在重构,第一步就是改名字。把有意义的名字给变量、函数、类换上,让别人一看名字就知道这个东西是干什么的。很多时候,名字改完了,代码的可读性就提升了一大半,根本不需要做其他改动。
比如有个函数叫processData,你看名字不知道它在干什么,点进去看了半天才发现它是在校验用户输入并且格式化。那你把它改成validateAndFormatUserInput,别人一看名字就知道这个函数是干什么的,根本不需要点进去看。这就是好命名的力量。
当然,起好名字不容易,需要你真正理解这段代码的业务含义。有时候一个好名字要想半天,但是这个时间花得值,因为后面所有看这段代码的人都会受益。
四、不要过度设计,为未来的需求买单是愚蠢的
这是很多程序员,特别是有点经验之后容易犯的错。重构的时候,总想着"以后可能会需要这个功能",于是就提前把各种扩展点、各种抽象层都加上了,结果代码变得非常复杂,而那些"以后可能需要"的功能,可能永远都不会来。
我以前也犯过这个错。有一次重构一个支付模块,我想着以后可能会支持多种支付方式,于是就搞了一套很复杂的抽象,策略模式、工厂模式、适配器模式全用上了,代码量翻了一倍。结果呢?这个项目到下线为止,就只用了一种支付方式,我那套复杂的抽象根本没用上,反而增加了维护成本,后来的人看我的代码看了半天都看不懂。
从那以后我就记住了,重构的时候只解决当前的问题,不要为未来不确定的需求提前设计。YAGNI原则(You Aren't Gonna Need It)说的就是这个,你以为你会需要,其实你不会。如果以后真的需要了,到时候再重构也不迟,那时候你有真实的需求,设计出来的东西反而更合理。
当然,这不是说完全不考虑扩展性,而是不要过度。一些显而易见的扩展点可以留,但是不要为了不确定的需求把代码搞得很复杂。简单的代码永远比复杂的代码好维护,这是真理。
五、技术债务要及时还,越拖越贵
重构的本质就是还技术债务。写代码的时候,因为时间紧、需求变等各种原因,我们经常会写一些临时方案、烂代码,这就是在借技术债务。借债没关系,但是一定要记得还,而且要尽早还,因为技术债务是有利息的,越拖越贵。
我见过很多项目,一开始代码还可以,后来因为各种原因,烂代码越来越多,技术债务越积越多,最后到了没人敢改的地步,改一个bug要花好几天,还容易引入新的bug。这时候再想重构,成本就非常高了,基本上只能推倒重来。
所以我的经验是,技术债务要及时还,每次改代码的时候,顺手把周围的烂代码重构一下,不用多,改一点点就行。时间长了,代码质量就慢慢上来了。这就是童子军规则:离开营地的时候,让它比你来的时候更干净。
当然,也不是所有技术债务都要马上还,要分优先级。核心模块、经常改的地方的债务要优先还,因为这些地方债务高了,每次改代码都要付出代价。而那些很少改的、稳定的模块,债务可以放一放,等有时间了再还。
六、写在最后
用了三年代码重构技巧,我才明白这些道理。
以上就是我这三年来做代码重构总结出来的一些经验和道理。重构不是什么高深的技术,它更多的是一种态度,一种对代码质量负责的态度。只要你愿意花时间,愿意小步快跑,愿意用测试保护自己,愿意给东西起好名字,不过度设计,及时还技术债务,你的代码质量一定会越来越好。
当然,这些道理都是我踩了很多坑才总结出来的,每个人的情况不一样,项目不一样,适合的方法也不一样。但是我相信,这些基本原则是通用的,希望能对大家有所帮助。
最后用一句话结尾:"代码是写给人看的,只是顺便能在机器上运行。"愿我们都能写出让人看得懂、愿意看的好代码。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录