怎么重构一个程序

什么是重构

摘自《重构:改善既有代码的设计》

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

重构的目的是使软件更容易被理解和修改。可以在软件内部做很多修改,但必须对软件可观察的外部行为只造成很小的变化,甚至不造成变化。与之形成对比的是性能优化,和重构一样,性能优化通常不会改变组件的行为,只会改变其内部结构。但是两者的出发点不同:性能优化往往使代码较难理解,但为了得到所需的性能你不得不这么做。

重构不会改变软件可观察的行为,重构之后的软件功能一往如此。

重构代码更像是整理代码,重构可以重构但是不要改变代码原本的功能,需要修改其内部结构,在不改变软件可观测行为的前提下,调整代码结构,当然这种修改肯定是有利的一面。提高软件的可理解性,降低变更成本。

为什么重构

首先需要明确的一点是,重构是一种经济适用性行为,而非道德使然,重构只有一个目的,就是让我们更快更好的开发代码,为什么需要重构,是因为现有的代码不能够提高开发效率,有时不但不能提高,还会降低很多很多。

其次重构是保证代码质量的一个极其有效的手段,不至于让代码腐化到无可救药的地步。项目在演进,代码不停地在堆砌。如果没有人为代码的质量负责任,代码总是会往越来越混乱的方向演进。当混乱到一定程度之后,量变引起质变,项目的维护成本已经高过重新开发一套新代码的成本,想要再去重构,已经没有人能做到了。

当我们遇到这段代码实在是太恶心了,花了很长时间才看懂,并且代码非常僵硬,而正好这个需求需要改动到这里,代码真的就像一坨屎。而最后是怎么处理的,我们通常是又给它加了一坨。

我们为什么会这么去做?因为重构会减慢当前任务速度,所以保持最快速度。

为什么这么做能干得又多又快?因为他将成本放到了未来。软件工程最大的成本在于维护,我们每一次代码的改动,都应该是对历史代码的一次整理,而非单一的功能堆积。
这样虽然能赢得现在,但终将失去未来,而这个失败的未来或许需要全团队与他一起买单。 这或许就是我们重构的最根本原因。

优秀的代码或架构不是一开始就能完全设计好的,就像优秀的公司和产品也都是迭代出来的。所以当我们做一个需求一定要回过头整理代码,保证代码的质量。傻瓜都能写出计算机可以理解的代码。唯有能写出人类容易理解的代码的,才是优秀的程序员。

除此之外重构对程序员本身的意义:

  1. 更快速的定位问题,节省调试时间。
  2. 最小化变更风险,提高代码质量,减少修复事故的时间,提高ROI。
  3. 得到程序员同行的认可,更好的发展机会。

重构场景

因为重构是一种经济实用性行为,所以重构的时候需要考虑成本,仅对必要的代码进行重构,某个工作行为如果重复三次就可以认为未来也会存在重复,因此通过重构使得下次工作更加高效,这是一种务实的作法。

举例,在添加一个新功能时,看之前的旧代码,如果有一些公共的功能可以提取出来,而这个功能,你新开发的功能又恰好用的上,那么重构这个功能可以帮助你梳理、理解代码,程序如果如此迭代下去,长久下来会提高团队的开发效率。

在《重构:改善既有代码的设计》一书中提到持续重构的理念。重构本来就不是一件应该特别拨出时间来做的事,重构应该随时随地的进行。重构不一定是需要大规模的展开的任务,重构应该是不断持续进行的,将任务拆解为多个具有完备性的任务,每周完成一个,每个任务的上线都不会引起问题,并使项目变得更好,这是一种持续重构的精神态度,是高效能程序员最应该具有的工作习惯。

不要代码烂到一定程度之后才去重构。当代码真的烂到出现开发效率低,招了很多人,天天加班,出活却不多,线上 bug 频发,领导发飙,中层束手无策,工程师抱怨不断,查找 bug 困难的时候,基本上重构也无法解决问题了。

我们要正确地看待代码质量和重构这件事情。技术在更新、需求在变化、人员在流动,代码质量总会在下降,代码总会存在不完美,重构就会持续在进行。应当时刻具有持续重构意识,才能避免开发初期就过度设计,避免代码维护的过程中质量的下降。

怎么重构

通读代码,分析现状,找到可重构代码。列出重构计划,确定重构目标,不要盲目的重构,明确的描述出重构后能达到的预期是什么。重构计划中必须给出测试验证方案,保证代码的可测试性,保证重构前与重构后软件的行为一致。
将重构任务当作项目来管理,对指定任务的人明确的排期和进度同步。这是重构大体上基本的步骤。

在重构项目时,我们可以借鉴分治思想,将重构任务拆分成每周都能见到效果的小任务,形成正反馈,定期开会同步进度,不断加强团队的重构意识。

下面是重构代码的一些常见问题和重构的一些建议

代码命名

  • 见名知意;避免增加认知负荷;
  • 考虑命名对整体架构的影响,要与整体的风格保持一致;

注释命名

复杂度与依赖传递

所谓复杂性,就是任何使得软件难于理解和修改的因素。

模糊性与依赖性是引起复杂性的2个主要因素,模糊性产生了最直接的复杂度,让我们很难读懂代码真正想表达的含义,无法读懂这些代码,也就意味着我们更难去改变它。而依赖性又导致了复杂性不断传递,不断外溢的复杂性最终导致系统的无限腐化,一旦代码变成意大利面条,几乎不可能修复,成本将成指数倍增长。

我们可以找到很多因素导致系统腐化的原因:

  • 想简单图省事,没有及时治理不合理的内容
  • 缺少匠心追求,对肮脏代码视而不见
  • 技术能力不够,无法应对复杂系统

除了上述内容外,还可以想到很多理由。但我们发现他们好像有一个共同的指向点 - 软件工程师,似乎所有复杂的源头就是软件工程师的不合格导致,所以其实一些罪恶的根因是我们自己?

软件复杂才是常态,不复杂才不正常。软件的复杂性是固有的,包括问题域的复杂性、管理开发过程的困难性、通过软件可能实现的灵活性与刻画离散系统行为的问题,这4个方面来分析了软件的发展一定伴随着复杂,这是软件工程这本科学所必然伴随的一个特性。
所以所有的软件架构万变不离其宗,都在致力解决软件的复杂性。

世间万物都需要额外的能量和秩序来维持自身,无一例外。没有外部力量的注入事物就会逐渐崩溃,这是世间万物的规律,而非我们哪里做得不对。所以我们才需要对系统进行维护甚至重构,降低系统的复杂度来保障系统的正常运行及降低维护的成本。

过度设计

过度设计就是增加复杂度的一种。

在初学编程的时候,只管埋头写程序,浑浑噩噩的进行开发。然而很快便发现事先做好设计可以节省返工的成本。许多人把设计看作软件开发的关键环节,而把编程看成是机械式的劳动。他们认为设计就像画工程图纸而编码就像施工。
因此需要把更更多得精力放在预先设计上,以免日后修改。

但是当过分的考虑程序未来所要面对的需求时,将陷入过度设计的陷阱,为了当下用不上的能力,而使程序变得复杂,这些设计很可能是现在未来都不需要的。

过度设计是一种负面的设计,所以要合理的进行设计,避免过度设计,把握好设计的尺度,适度设计。下面是避免过度设计的几点建议:

  • 将注意力集中在应用程序的核心功能上,以满足用户的基本需求。避免在边缘功能上过度设计和开发,这些功能可能会增加程序的复杂性和难度。尽量采用简单的解决方案,而不是过度复杂的设计。
  • 设计的目的之一是为产品和用户带来更多的价值和服务,因此必须保证所有的设计都有其存在的“价值”,这个“价值”可以是为用户带来更好的体验,也可以是是为产品带来更多的收益。任何没有“价值”的设计,就是“过度设计”,可以直接删除。

要明确一点,设计是产品实现用户需求之间的一种手段,不是最终的目的,不能为了设计而设计,添加一些乱七八糟的功能,那样产品的用户体验也是很差的。在设计中应当确保应用程序的核心功能得到优先关注,避免过度边缘化。

没有时间重构

这是重构所面临最多的借口,是自己也是团队的借口。

为此必须要明确重构是经济行为而不是一种道德行为,重构使得开发效率变得更高,因此仅对必要的代码进行重构,某个工作行为如果重复三次就可以认为未来也会存在重复,因此通过重构使得下次工作更加高效,这是一种务实的作法,而重构不一定是需要大规模的展开的任务,重构应该是不断持续进行的,将任务拆解为多个具有完备性的任务,每周完成一个,每个任务的上线都不会引起问题,并使项目变得更好,这是一种持续重构的精神态度,是高效能程序员最应该具有的工作习惯。

如果你在给项目添加新的特性,发现当前的代码不能高效的完成这个任务,并且同样的任务出现三次以上,那么这时你应该先重构,再开发新特性。

  • 35
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值