为什么我们重构?

时间:2009-05-06 09:15:11

标签: refactoring

我想知道我们进行重构并证明其合理性的原因。我看到很多人对重构的想法感到不满。 Refactoring was variously described as:

  1. 前期不足的结果 设计。

  2. 无纪律的黑客攻击

  3. 一种危险的活动,不必要地冒着破坏稳定的风险 工作代码

  4. 浪费资源。

  5. 导致我们重构代码的责任原因是什么?

    我在how-often-should-you-refactor也找到了类似的问题,它没有提供重构的原因。

27 个答案:

答案 0 :(得分:36)

  

为什么我们重构?

因为编写代码时没有实际替代。没有多少前期规划或经验可以替代实际的代码编写。这就是整整一代人(称为瀑布)艰难学习的方法。

一旦你开始编写代码并处于其中间,你就可以在较低的层次上推断它的工作方式 注意逃避的代码(性能,可用性或正确性)更高的设计观点。

重构正在完善。

问问自己:为什么画家会用刷子在同一个地方做多次击打?

答案 1 :(得分:14)

重构是支付technical debt的方式。

答案 2 :(得分:7)

为了保持可维护的代码库?

代码读取比写入更多,因此必须具有可读,易懂和可维护的代码库。当您看到编写或设计不佳的内容时,可以对其进行重构以改进代码的设计。

你也定期打扫你的房子,不是吗?虽然这可能被认为是浪费时间,但为了让你的房子保持清洁是必要的,这样你才能拥有一个良好的生活环境。

答案 3 :(得分:7)

我想简要介绍一下你的三点。

<强> 1。 “前期设计不足的结果”

常识(以及几本书和博客)告诉我们,我们应该努力寻求最简单,最干净的设计来解决特定问题。虽然编写一些代码很可能没有充分的工作来开发对需求和问题领域的理解,但是在编写代码时,“糟糕的代码”可能更“普遍”。相反,它已经不够了。

需求变更,设计必须支持其他功能。预先预测未来的一些变化并不是不合理的,但是McConnell等。当对这种方法没有明确和迫切的需求时,要正确地警告高级别,过于灵活的设计。

第3。 “一种危险的活动,不必要地冒着破坏稳定工作代码的风险”

嗯,是的,如果做得不正确的话。在你试图对工作系统进行任何重大修改之前,你应该采取适当的措施来确保你不会造成任何伤害 - 几乎是一种“发展Hippocratic oath”。

通常情况下,这将通过文档和测试的混合来完成,而且通常情况下,代码会胜出,因为它是对实际行为的最新描述。实际上,这可以转化为具有unit test套件的适当覆盖率,因此如果重构确实引入了意外问题,则会识别并解决这些问题。

显然,当你寻求重构时,你将打破一定数量的测试,尤其是因为你正在尝试修复一些破解的代码合同。但是,如果您有适当的机制来发现意外错误,那么完全有可能重犯而不受惩罚。

<强> 4。 “浪费资源”

其他人提到了technical debt的概念,简而言之,就是随着时间的推移,这种系统的复杂性逐渐增加,并且必须通过重构和其他技术,以合理地促进未来的发展。换句话说,有时你必须咬紧牙关并继续推进你一直推迟的改变,因为否则当你在这个区域添加新东西时,你会变得非常糟糕。

显然,有时间和地点可以还清这些东西;你不会尝试偿还贷款,直到你有现金去做,而且在开发的关键阶段,你无法负担得起。然而,通过决定解决代​​码库中的一些问题,可以节省未来的开发时间,从而节省资金,甚至可以进一步节省未来,避免不得不放弃或完全重写某些超出部分的成本你的理解。

答案 4 :(得分:6)

如果您的代码是

,则可能需要重构
  • 低效
  • 越野车
  • 难以延伸
  • 难以维护

这一切归结为原始代码不是很好,所以你改进它。 如果你有合理的单元测试,它根本不应该是危险的。

答案 5 :(得分:3)

因为后见之明比先见之明更容易。

软件是人类创造的最复杂的东西之一,因此事先考虑一切并不容易。对于大型项目来说,团队甚至不可能(至少是由人类组成的团队;))在实际开始开发之前考虑所有事情。

另一个原因是软件没有构建,它正在增长。这意味着软件可以而且必须适应不断变化的需求和环境。

答案 6 :(得分:2)

正如Martin Fowler所说,对于软件变更的要求,唯一令人惊讶的是任何人都对此感到惊讶。

要求将发生变化,将要求提供新功能。这是一件好事。增强工作大部分时间都会成功,当它们失败时,它们会失败,所以有预算可以做更多。大型前端设计项目经常失败(一个统计数据将失败率设为66%),所以要避免它们。避免它们的方法是为第一个版本设计足够的内容,并且在添加增强功能时,重构一下它看起来就像系统首先要做的那样。可以执行此操作的项目的生命周期(当您发布数据格式或API时出现问题 - 一旦您上线,您再也不能保持原始状态)是无限期的。

回应这四点,我想说的是一个避免重构要求的过程:

  1. 一个没有任何变化的静态世界 这样前期设计就可以达到 完美的非移动目标。
  2. 威尔     导致丑陋的黑客工作     设计不存在的缺陷     重构。
  3. 会导致危险         代码重复,因为害怕         更改现有代码集。
  4. 威尔             浪费资源而不是工程             问题和建立大型设计             预期的文物             永远不会结束的要求             建造,造成大量             代码和复杂的拖动             在没有提供的情况下投射             值。
  5. 但是,有一点需要注意。如果你没有适当的支持,在一个简单案例的自动化工具中,在更复杂的情况下进行彻底的单元测试,它会受到伤害,会引入新的bug,你会产生一种(非常理性的)恐惧做得更多。重构是一个很好的工具,但它需要安全设备。

答案 7 :(得分:1)

一个简单的答案是,要求发生变化。无论你的设计多么优雅,以后的一些要求都不会买。

答案 8 :(得分:1)

另一种需要重构的场景是TDD。 TDD的教科书方法是只编写通过测试所需的代码,然后将其重构为更好的代码。

答案 9 :(得分:1)

虽然很多其他人已经说过完全正确的理由,但这是我的:

因为它是有趣。这就像在障碍赛中击败自己的时间,在自行车比赛中拥有更强的二头肌或者在你选择的比赛中提高你的高分。

答案 10 :(得分:1)

  1. 对要求的理解不足:

    如果开发人员对要求没有清楚的了解,那么最终的设计和代码就无法满足客户。随后要求变得更加清晰,重构变得至关重要。

  2. 支持新要求。

    如果组件是旧的,在大多数情况下它将无法处理根本的新要求。然后,重要的是进行重构。

  3. 现有代码中有很多错误。

    如果你在办公室里花了很长时间来修复特定组件中的一些令人讨厌的错误,那么它最早就会成为重构的自然选择。

答案 11 :(得分:1)

...因为编码就像园艺。您的代码库会随着时间的推当时的好主意通常看起来像是一个糟糕的设计,现在什么是好的设计可能不会是最佳的。

代码永远不应被视为永久性工件,也不应被视为过于神圣。应该通过测试获得信心,重构是促进变革的机制。

答案 12 :(得分:0)

第一点是否重要?如果你正在重构,那么前期设计显然是有缺陷的。不要浪费时间担心原设计中的缺陷;这是个老消息。重要的是你现在拥有的,所以花时间重构。

答案 13 :(得分:0)

我重构,因为适当的重构使维护变得更容易。我不得不维持一堆糟糕,糟糕的代码,而且我不想把我为其他人写的任何东西都交给我维护。

臭味代码的维护成本几乎总是高于甜味代码的维护成本。

答案 14 :(得分:0)

重构是为了使代码更容易理解/记录。

  • 给一个方法一个更好的名字 - 也许以前的不清楚或不正确。
  • 为变量提供更具描述性/更好的名称。
  • 将一个非常长的方法分解成许多较小的方法,代表解决问题所涉及的步骤。
  • 将类移动到新包(命名空间)以协助组织。
  • 减少重复的代码。

答案 15 :(得分:0)

在方法和类中,大型重构(重组模块,类层次结构,接口)和“单元”重构之间存在差异。

每当我触摸一段代码时,我都会进行单元重构 - 重命名变量,提取方法;因为实际上看到我面前的代码可以为我提供更多信息以使其更好。有时重构也有助于我更好地理解代码正在做什么。就像写作或绘画一样,你从头脑中提取出一个模糊的想法;把粗糙的骨架放在纸上;然后进入代码。然后,您可以在代码中细化粗略的想法。

使用C#中的ReSharper等现代重构工具,这种单元重构非常简单,快速和简单。风险很低。

大型重构更难,破坏更多东西,并需要与团队成员沟通。当这些需要发生时,每个人都会清楚地知道 - 因为要求已经发生了很大变化,以至于原始设计不再适用 - 然后它们应该像新功能一样进行规划。

我的最后一条规则 - 只有您正在实施的重构代码。如果不需要更改代码的功能,那么它就足够了。不需要进一步的工作。

为了重构而避免重构;那只是重构!

答案 16 :(得分:0)

根据我自己的个人经验,我重构,因为我发现如果我按照我想要的方式制作软件,那么创建一些东西需要很长时间。

因此,我重视开发软件而非干净代码的实用主义。一旦我运行了一些东西,我就会开始以它应该的方式重构它。毋庸置疑,代码永远不会变成一块不可读的肚子。

只是旁注 - 我从史蒂夫麦克康内尔读了一些青少年材料后,我获得了软件工程学位。我喜欢设计模式,良好的代码重用,精心设计的设计等等。但是我发现在我自己的项目中,从这个角度来设计事物只是不起作用,除非我是我正在使用的技术的绝对专家(从来就不是这样)

答案 17 :(得分:0)

我找到了代码设计和实现,特别是对于不熟悉的大型项目来说,这是一个学习过程。

项目的范围和要求会随着时间的推移而变化,这会对设计产生影响。可能是在花了一些时间实施您的产品之后,您会发现您计划的设计并非最佳。也许客户增加了新的要求。或者您可能正在为旧产品添加其他功能,并且您需要重构代码以充分提供此功能。

根据我的经验,代码编写得很糟糕,重构已成为防止产品失效并确保其可维护/可扩展的必要条件。

我相信一个迭代设计过程,尽早进行原型设计是一种在以后最小化重构的好方法。这也允许您尝试不同的实现来确定哪个最合适。

不仅如此,您可以使用新的想法和方法。为什么坚持使用旧的,错误的代码,如果可以改进它可能会成为问题?

简而言之,项目将改变加班时间,这需要改变结构以确保其符合新的要求。

答案 18 :(得分:0)

所有要点都是人们重构的原因的常见描述。我会说人们应该重构的原因在于第1点:大设计前线(BDUF)几乎总是不完美的。您在构建系统时了解系统。在试图预测可能发生的事情时,您经常会建立复杂的解决方案来处理从未发生过的事情。 (YAGNI - 你不需要它)。

因此,更好的解决方案是设计您知道自己需要的系统部件,而不是BDUF方法。遵循single responsibility principle的原则,使用inversion of control / dependency injection,以便您可以在需要时替换部分系统。

为您的组件编写测试。然后,当您的系统要求发生变化或者您发现初始设计中存在缺陷时,您可以重构和扩展您的代码。由于您已经进行了单元测试和集成测试,因此您将了解重构是否以及何时会破坏某些内容。

答案 19 :(得分:0)

重构是任何敏捷软件开发方法的核心组件。

除非您完全了解项目的所有要求和技术限制,否则您无法获得完整的前期设计。在这种情况下,不使用传统的瀑布式方法,您可能最好使用敏捷方法 - 敏捷方法专注于快速适应不断变化的现实。如何在不进行重构的情况下调整源代码?

答案 20 :(得分:0)

我重构,因为在没有重构的情况下,随着时间的推移向代码库添加新功能变得越来越难。如果我有要添加的功能A,B和C,功能C将尽快完成,如果我在A和B功能之后需要时间重构,我会更少痛苦和痛苦。我很开心,我的老板更快乐,我们的客户更快乐。

我认为值得重申,在任何涉及重构的对话中,重构都是可验证的行为保留。如果在“重构”结束时你的程序有不同的输出,或者如果你只想,但不能证明它具有相同的输出,那么你所做的就是不重构。 (这并不意味着它没有价值或者不值得做 - 也许这是一种改进。但它不是重构,不应该与它混淆。)

答案 21 :(得分:0)

对我进行重构就像打扫我的桌子一样;它创造了一个更好的工作环境,因为随着时间的推移,它会变得混乱。

答案 22 :(得分:0)

人为错误,在开发软件时,你总是会犯错误。从一开始就创建一个好的设计有很大帮助,在团队中拥有熟练的程序员也是一件好事,但他们总是会犯错误,并且会有难以阅读,紧密耦合或无法运行的代码等重构是一种工具,可以在它们已经发生时修复这些缺陷。你应该永远不要停止工作来防止这些事情发生,但是当它们发生时,你可以修复它们。

答案 23 :(得分:0)

让疯狂的事情变得清醒。

当代码在复制+粘贴以及缺乏架构指导的情况下遭受如此多的重构时,我主要重构,理解代码的行为类似于重新组织它并删除重复。

答案 24 :(得分:0)

无论出于何种原因,当我创建或找到一个滚动屏幕的功能时,我知道是时候坐下来考虑是否应该重构 - 如果我需要滚动整个页面才能进入作为一个整体的功能,它可能不是可读性或可维护性的光辉典范。

答案 25 :(得分:0)

Upfront:当a)工具支持时,重构不需要是危险的; b)你有一个可以在重构后运行的测试套件,以便检查软件的功能。

重构的一个主要原因是,在某些时候,您发现代码被多个代码路径使用,并且您不希望复制(复制和粘贴)但重用。在您在该代码中发现错误的情况下,这一点尤为重要。如果您已将重复的代码重构为自己的方法,则可以修复该方法并完成。如果您复制并粘贴代码,很可能无法修复此代码发生的所有位置(只需考虑具有多个成员和数千行代码的项目)。

你当然不应该仅仅因为重构而进行重构 - 那么它实际上是浪费资源。

答案 26 :(得分:0)

我重构因为:

  1. 我的代码通常不是第一次使用。
  2. 后见之明通常是20-20。
  3. 我的代码将更容易为下一个人维护。
  4. 我对自己留下的工作感到非常自豪。
  5. 我相信现在花在时间上可以节省更多的时间(和金钱)。