你如何重构一个经常被编辑的类?

时间:2009-02-24 21:35:55

标签: language-agnostic version-control refactoring

随着时间的推移,我的团队已经创建了一个中心课程,负责处理责任的集合,并运行超过8,000行,所有这些都是手写的,而不是自动生成的。

任务已经下降。我们需要重构怪物类。计划的最大部分是将功能类别定义到他们自己的类中,并与怪物类建立一种关系。

这意味着目前大量的参考资料都是这样的:

var monster = new orMonster();
var timeToOpen = monster.OpeningTime.Subtract(DateTime.Now);

很快就会读到这样的内容:

var monster = new Monster();
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now);

问题是:我们如何在地球上协调这样的改变?每个商务舱都提到“orMonster”垃圾。在代码中的数千个地方调用了一些方法。我们保证,只要我们有这样的机会,团队中的其他人(可能是多个人)就会检查出调用.OpeningTime属性的代码

如何在没有生产力停滞的情况下协调如此大规模的变革?

12 个答案:

答案 0 :(得分:27)

您应该让旧方法调用新方法。然后随着时间的推移更改对旧方法的引用,以调用新方法。更改所有客户端引用后,您可以删除旧方法。

有关更多信息,请参阅Martin Fowler的经典Move Method中的Refactoring

答案 1 :(得分:10)

您可以做的一件事是暂时将代理方法留在将委托给新方法的怪物类中。大约一周后,一旦您确定所有代码都在使用新方法,那么您可以安全地删除代理。

答案 2 :(得分:7)

我之前通过继续并重构代码来处理这个问题,然后添加与旧签名匹配的方法,将旧调用转发给新方法。如果将“Obsolete”属性添加到这些临时方法,则仍将使用旧方法调用和新方法调用构建代码。然后,随着时间的推移,您可以返回并升级调用旧方法的代码。这里的不同之处在于,您将在构建期间获得“警告”,以帮助您找到需要升级的所有代码。

答案 3 :(得分:6)

我不确定您使用的语言是什么,但在.Net中,您可以创建编译器警告,这样您可以将旧引用保留一段时间,以便它们按预期运行,但会为其他开发人员发出警告看。

http://dotnettipoftheday.org/tips/ObsoleteAttribute.aspx

答案 4 :(得分:4)

在分支机构中开发您的更改。将一部分代码分解为一个新类,在客户端基础上进行更改,彻底测试,然后合并。

将破坏集中在合并时 - 而不是整个开发周期。

将此与帕特里克对have the monster call the small monsters的建议相结合。如果您的合并客户端代码中断了对该客户端的更改,那么这将让您轻松还原。帕特里克说,一旦你证明没有人使用它,你就能够移除怪物的方法(现在存根)。

我还回应了几个海报的建议,直接暴露出破碎的类 - 而不是通过怪物。为什么只涂一半治疗?通过同样的努力,您可以完全治愈。

最后:写单元测试。写很多单元测试。哦,男孩,你需要进行单元测试才能安全地拉下这个。我提到你需要进行单元测试吗?

答案 5 :(得分:3)

保留旧方法并转发到新方法(正如其他人所说),但也会在转发方法中发送一条日志消息,提醒自己将其删除。

您可以添加评论,但这很容易错过。

答案 6 :(得分:2)

建议使用nDepend等工具来识别对类方法的所有引用。 nDepend的输出可用于更好地了解如何对方法进行分组。

答案 7 :(得分:2)

var monster = new Monster();
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now);

我不确定将它分开并只是公开提供它的一部分就更好了。这违反了得墨忒耳法,可能会导致NullReference痛苦。

我建议在不涉及怪物的情况下将计时员暴露给人。

如果有什么事情你可以很好地分析API,看看你可以在怪物中切割和封装什么。当然,让怪物玩具玩,而不是让怪物做所有的工作本身是一个很好的电话。主要工作是定义玩具怪物需要简化他的工作。

答案 8 :(得分:1)

不要重构它。

重新开始并遵循demeter定律。创建第二个怪物类并从头开始。当第二个怪物类完成并工作时,则替换第一个怪物的出现。交换掉它。希望他们共享一个界面,或者你可以实现这一点。

而不是这个:“monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now)”

执行此操作:monster.SubtractOpeningTime(DateTime.Now)。不要用点符号(因此是demeter)来杀死自己

答案 9 :(得分:1)

有几个人就重构本身的编排提供了很好的答案。这是关键。但是你也问过协调多人之间的变化(我认为这是你问题的关键)。你使用什么源代码控制?像CVS,SVN等一样可以同时处理来自多个开发人员的传入更改。使其顺利进行的关键是每个人必须使他们的提交细化和原子化,并且每个开发人员应经常提取其他人的提交。

答案 10 :(得分:1)

我将首先使用partial class将单个怪物类拆分为多个文件,将方法分组。

当你分成文件时,你需要阻止任何编辑怪物类的人。

从那时起,您可能会减少合并冲突,因为每个文件的编辑次数会减少。然后,您可以更改怪物类中的每个方法(每个签入一种方法)来调用您的新类。

答案 11 :(得分:-2)

如此庞大的课程确实是一个问题。由于它变得如此之大并且没有人感到不舒服,项目政策肯定存在问题。我会说你应该分成两对并进行编程。为每对程序员创建一个分支。重构工作1-2天。比较您的结果。这将帮助您避免重构从开始到错误方向的情况,最终导致需要从头开始重写怪物类。