重构是提高质量的重要工具。许多开发方法都依赖于重构,尤其是对于敏捷方法,在更多计划驱动的组织中也是如此。但事实上,是否以重构来处理设计中的某些问题呢?是否存在重构的障碍呢?
存在质量问题而不进行重构的理由可以分为以下几类:
对所需资源的关注是不进行重构的一个经常被提到的原因。最常提到的资源是时间,比如“DDL不允许”,“有时候就是没有时间”或者单纯的“就是没有时间”。
同样经常被提到的还有更改所涉及的风险,尤其是引入新的错误或其他问题,比如“这种重构很耗时间,而且引入错误的风险很大”,以及“反正还能用,管它干嘛”等等。
另一个问题是进行更改的难度,比如“继承很难正确重构”和“这种重构通常很困难”等等。
虽然重构可能会带来好处,但也有成本,投资回报率也必须考虑,例如“必须再次权衡成本和好处,在承担重构、重测试等方面的成本之前,必须明确收益。”
项目的特点限制了重构的能力。例如,必须实现超出限制的第三方接口,担心任何潜在的更改对系统其他部分的影响,对代码的熟悉程度,处理遗留代码,以及缺乏其他支持(如测试套件) ,尤其是“大量遗留代码库使重构变得困难”; “如果没有测试方法,系统可能被破坏”; 以及“没有时间进行重构的预算。”等等
开发者并不总是能控制他们的时间使用。老板或客户更有发言权,比如“想要重构,但老板不喜欢”、“只关注截止日期的老板”、“客户不会为此付钱”等等。
工具支持不足也被认为是不重构的一个原因,然而,这些的工具并不是那些进行重构的那些工具,某些重构非常痛苦,实际上是缺乏工具的支持。微软对 Windows 进行了某种形式的重构,确定了执行重构的挑战,例如管理组件间和分支间的依赖关系以及维护向下兼容,也许最令人惊讶的主题是完全不关心重构工具支持的存在和质量。
重构并非难在怎么做,而是难在何时开始做。对于一个高速发展的企业来说,停下来做重构从来就不是一个可接受的选项,“边开飞机边换引擎”才是公司想要的。当代码还不是很混乱的时候,重构的必要性不高,相比不小心重构出错导致熄火的风险来说,得过且过可能反而是一个明智之选。于是,各种技术债就这样慢慢积累起来,直到业务因为各种技术债快跑不动的时候,才不得不用一些激进的重构手段快速的解决历史顽疾。
在软件工程中,往往使用每类加权方法(WMC)和继承树(Depth of Inheritance Tree,DIT) 来度量面向对象的设计,这些度量常常被表示为可能的设计问题,即WMC 或 DIT 的值越高,就越有可能出现问题。WMC 是类的大小度量的一种形式,最简单的形式是类中方法数量的计数。DIT 捕获继承层次结构的一个特征,它是从类到层次结构根的最长路径长度。然而,对软件系统的测量表明,有些类具有很多方法,或者在层次结构中非常深。它就需要重构么?
实际上,工程师更倾向于限制类的深度,而不是方法的数量,但是当超过某个深度限制时,开发者倾向于不做任何事情。
很多时候,无法确定 ROI 才是一个障碍,比如“如果不能对需要多长时间做出合理的估计,就不会管它。”也就是说,决定不重构并不是因为它被认为是一个坏主意,而是因为它的收益不确定。在特定情况下进行重构时,通常很少或根本不知道实际的 ROI 是什么。
一个潜在的障碍是很难将重构目标转化为重构操作。需要的是一个决策支持系统,使从业人员能够量化长期和短期效益。这将有助于为决定所需资源是否合理或是否容忍潜在风险提供信息,还允许开发人员和管理人员就是否重构做出明智的选择。
具体地,Martin Folwer在《重构》一书中提到,需要识别坏味道,并提出了进行重构的4种情况,即关于重构的CRUD:
也就是说,设计没有表达出对需求的最新理解,或者需求没有被很好地实现,而且已经发现更好的实现方法,以及发现了一个能使某个设计变得简单、灵活的方法。实际上,重构的时机只是做出是否重构的判断时机。
另外,使用重构工具的两个好处是错误率更低,所需时间更少,因此好的工具支持应该在一定程度上解决开发人员的问题。只有当重构的决定已经做出时,重构工具的支持才会起作用。实际上,不使用自动化进行重构的原因可能包括信任、可预测性和复杂性。
无论软件设计质量问题是如何识别的,开发者都无法通过重构来根本消除这些问题。减少甚至消除重构的障碍有可能显著提高软件质量。一种方法是提供目标导向的重构支持,而不是操作导向的重构支持。另一个办法是更好地量化效益,从而更好地告知是否重构的决定。