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

时间:2009-06-22 07:15:57

标签: refactoring

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

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

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

10 个答案:

答案 0 :(得分:64)

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

  

重构是一种受控制的技术   改善设计   现有代码库。它的本质是   应用一系列小   保持行为的转变,   每个“太小而不值得   做“。然而累积的影响   这些转变中的每一个都是   非常重要。通过他们做   你可以减少风险的小步骤   引入错误。你也避免   让你的系统崩溃   进行重组 - 哪个   允许你逐步重构一个   系统在很长一段时间内   时间。

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

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

答案 1 :(得分:29)

Fowler在代码更改和不执行代码更改之间划清界限。他把那些没有的人称为“重构”。这个的一个重要区别,因为如果我们将我们的工作分为重构和非重构代码修改活动(Fowler称之为“戴着不同的帽子”),我们可以应用不同的,适合目标的技术。 / p>

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

  • 我们所有的单元测试都应该通过 并在修改后
  • 我们不需要修改任何 测试,或写任何新的
  • 我们期待更清洁的代码 完成
  • 我们不期待新的行为

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

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

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

答案 2 :(得分:17)

提出我的观点:

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

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

肯定没有:重写大块明显违反了“小增量”部分。重构通常用作重写的相反:而不是再次执行,改进现有。

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


示例:想象一下,你有一个报告随机化模块,它通过使用向量而减慢速度。你已经分析过,矢量插入是瓶颈,但不幸的是,模块依赖于许多地方的连续内存,这样当使用列表时,事情会无声地破坏。

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

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


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

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

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

答案 3 :(得分:12)

考虑到Martin Fowler的定义,

  

重构是一种训练有素的技术   重组现有的机构   代码,改变其内部结构   不改变其外在行为。

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

  

他们也提出了类似的建议   改变数据结构(如Java   LinkedList到ArrayList),正在改变   算法(使用合并排序   泡泡排序),甚至重写   大块代码作为重构。

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

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

答案 4 :(得分:8)

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

答案 5 :(得分:8)

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

重构的典型案例是

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

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

答案 6 :(得分:4)

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

  

代码重构是   更换电脑的过程   程序的内部结构没有   修改其外部功能   行为或现有功能,在   为了改善内部   非功能性的   软件,例如改进代码   可读性,简化代码   结构,改变代码遵守   一个给定的编程范例,到   提高可维护性,改进   表现,或改善   可扩展性。

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

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

答案 7 :(得分:2)

disagree

  

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

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

答案 8 :(得分:1)

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

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

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

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

答案 9 :(得分:0)

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

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