什么是重构?什么是修改代码?

问题描述 投票:69回答:10

我知道重构是“改变程序的结构,以便不改变功能”。我正在与我在大学的最后一年项目中与我合作的一些人谈话,我很惊讶他们有更广泛的(因为想要更好的词)重构的观点。

我认为重构就像提取方法和重命名类一样。他们还建议改变数据结构(如Java LinkedListArrayList),改变算法(使用合并排序而不是冒泡排序),甚至重写大块代码作为重构。

我很确定他们错了,但我无法给出一个很好的理由,因为他们的建议确实改变了程序(并且可能使其更好)而不改变其行为。我是对的,更重要的是,为什么?

refactoring
10个回答
66
投票

Martin Fowler的"Refactoring: Improving the Design of Existing Code"也许是参考:

重构是一种用于改进现有代码库设计的受控技术。它的本质是应用一系列小的保持行为的变换,每个变换“太小而不值得做”。然而,这些变换中的每一个的累积效应是非常显着的。通过小步骤完成它们可以降低引入错误的风险。您还可以在执行重组时避免系统损坏 - 这允许您在较长时间内逐步重构系统。

重构与单元测试密切相关。在重构之前编写测试,然后在重构中有一个置信度(与测试的覆盖率成比例)。

一个很好的参考是:Information about Refactoring


0
投票

重构=改进非功能性需求,同时保持功能性不变。

非功能性要求=模块化,可测试性,可维护性,可读性,关注点分离,liskov原则等等......


29
投票

Fowler在代码更改之间绘制了一条简洁的界限,而不会影响其行为。他把那些没有的人称为“重构”。这是一个重要的区别,因为如果我们将我们的工作分为重构和非重构代码修改活动(Fowler称之为“戴着不同的帽子”),我们可以应用不同的,适合目标的技术。

如果我们正在进行重构或保留行为的代码修改:

  • 我们所有的单元测试都应该在修改之前和之后通过
  • 我们不需要修改任何测试,也不需要编写任何新测试
  • 我们希望在完成后能够使用更清晰的代码
  • 我们不期待新的行为

如果我们正在进行改变行为的代码修改:

  • 我们期待新的行为
  • 我们应该写新的测试
  • 我们完成后可能会得到更脏的代码(然后应该重构它)

如果我们忽略了这种区别,那么我们对任何特定代码修改任务的期望都会变得混乱和复杂,或者无论如何都比我们注意到它更加混乱和复杂。这就是为什么这个词及其含义很重要。


17
投票

提出我的观点:

小的,增量的更改使代码处于比找到的状态更好的状态

肯定是:“化妆品”的变化与功能没有直接关系(即它不能作为变更请求收费)。

绝对不会:重写大块明显违反了“小的,增量的”部分。重构通常用作重写的反面:不再重复,而是改进现有的重写。

绝对可能:取代数据结构和算法在某种程度上是一种边界情况。这里决定性的差异IMO是一个小步骤:​​准备好交付,准备好处理另一个案例。


示例:假设您有一个Report Randomizer模块,它通过使用向量来减慢速度。您已经分析过,矢量插入是瓶颈,但不幸的是,模块依赖于许多地方的连续内存,因此在使用列表时,事情会无声地破坏。

重写意味着将模块从头开始扔出建筑物更好更快,只需从旧建筑中挑选一些。或者编写一个新的核心,然后将其装入现有的对话框中。

重构将意味着采取小步骤删除指针算术,以便切换。也许你甚至创建一个包含指针算术的实用函数,用对该函数的调用替换直接指针操作,然后切换到迭代器,以便编译器抱怨仍然使用指针算术的地方,然后切换到list,然后删除溃疡功能。


背后的想法是代码本身变得更糟。在修复错误和添加功能时,质量会以小步骤衰减 - 变量的含义会巧妙地改变,函数会获得一个额外的参数来打破隔离,循环会变得复杂等等。这些都不是真正的错误,你可以告诉行计数会使循环变得复杂,但是会损害可读性和维护性。

同样,更改变量名称或提取函数也不是他们自己的有形改进。但总而言之,他们对抗缓慢的侵蚀。

就像一块鹅卵石墙,每天都落在地上。每天,一个路人会把它捡起来并放回去。


12
投票

考虑到Martin Fowler的定义,

重构是一种规范的技术,用于重构现有的代码体,改变其内部结构而不改变其外部行为。

......我认为你显然是对的。

他们还建议改变数据结构(如Java LinkedList到ArrayList),改变算法(使用合并排序而不是冒泡排序),甚至重写大块代码作为重构。

将算法更快地更改为某种东西显然不是重构,因为外部行为已经改变了! (再说一遍,如果效果永远不会引起注意,也许你可以把它称之为重​​构 - 而且还要过早优化。:-)

这是我的一个宠儿;当人们使用sloppily这个术语时很烦人 - 我甚至遇到过一些人可能会随便使用重构进行基本上任何改变或修复。是的,这是一个时髦而酷炫的流行语,但是对于简单的旧术语,如改变,重写或性能改进,没有任何问题。我们应该在适当的时候使用它们,并在您真正改进软件内部结构的情况下保留重构。特别是在开发团队中,使用通用语言准确地讨论您的工作很重要。


8
投票

我认为你是对的,但争论一个词的含义并不是特别有趣或富有成效。


8
投票

如果一段代码的接口发生了变化,那么我认为这不仅仅是重构。

重构的典型案例是

  • “哦,我所有的单元测试都运行了,但我认为我的代码可以变得更干净”
  • 将代码更改为更易读/更清晰/更有效
  • 重新运行单元测试(不更改测试)并检查它们是否仍然有效

这意味着重构一词与您正在讨论的界面有关。即你可以重构一个界面背后的代码,同时更广泛地改变另一个界面的代码(也许这种区别是你和你的同事之间的混淆?)


4
投票

http://en.wikipedia.org/wiki/Code_refactoring

代码重构是在不修改其外部功能行为或现有功能的情况下更改计算机程序内部结构的过程,以便改进软件的内部非功能属性,例如提高代码可读性,简化代码结构,更改代码遵循给定的编程范例,提高可维护性,改进性能或提高可扩展性。

我同意重构代码确实包括破坏现有代码。只要确保你有单元测试,这样你就不会引入任何错误,其余的代码也会编译。使用像Resharper for C#这样的重构工具使这很容易!

  • 使代码更容易理解
  • 清理代码并使其更整洁
  • 删除代码!应删除冗余,未使用的代码和注释
  • 提高绩效
  • 制作更通用的东西。从最简单的事情开始,然后重构它以使其更容易测试/隔离或通用,以便它可以通过多态性以不同的方式工作
  • 保持代码干 - 不要重复自己,因此重构会话可能涉及重复代码并将其重构为单个组件/类/模块。

2
投票

disagree

在软件工程中,“重构”源代码意味着在不改变其整体结果的情况下改进它[...]

您已经知道用于重构子集的更精确的术语,是的,它是一个非常通用的术语。


1
投票

我认为没有人可以从“重构”一词的过于强烈的定义中受益。你如何看待它和你的同事之间的界限是模糊的,可以根据许多事实更接近他们或你的观点。因为它是动态的,所以我们试着去定义它。首先定义您尝试重构的系统或子系统的边界。

如果是方法,请保留名称,输入参数,返回值的类型以及可能抛出的语句。应用方法内的所有更改,而不更改在外部查看的方式。

如果您重构类修复其公共API并使用重命名变量,则提取方法和所有其他可用技术会将类更改为更具可读性和/或更高性能。

如果要重构的代码部分是包或模块,则在其中进行重构可能会重命名类,删除,引入接口,将代码推送/拉入超类/子类。

© www.soinside.com 2019 - 2024. All rights reserved.