2009-02-24 45 views
10

随着时间的推移,我的团队创建了一个中央班级,负责处理责任集聚并运行超过8000行,全部都是手写的,而不是自动生成的。你如何重构一个不断被编辑的类?

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

这意味着,大量的参考文献,目前这样写的:

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属性

如何协调如此大规模的更改而无需生产率磨削停下来?

+0

使用Visual Studio,右键单击“查找所有引用...”并替换。 – core 2009-03-19 09:30:36

回答

27

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

欲了解更多信息,请参阅Martin Fowler的经典Refactoring中的Move Method

+0

这个。如果你的语言具有该功能,则将其标记为不赞成使用。它们仍然可以工作,但是它们会在IDE中出现丑陋的下划线/删除线,并在控制台中添加警告。 – 2009-03-17 17:37:18

10

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

1

不要重构它。

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

的,而是这样的: “monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now)”

这样做:monster.SubtractOpeningTime(DateTime.Now)。不要用点符号自杀(因此是demeter)

2

建议使用nDepend这样的工具来识别对类方法的所有引用。 nDepend的输出可以用来给你一个关于如何分组方法的更好的想法。

7

我之前通过继续并重构代码来处理此问题,但之后添加了匹配将新调用转发给新方法的旧签名的方法。如果将“Obsolete”属性添加到这些临时方法中,那么您的代码仍将使用旧方法调用和新方法调用进行构建。然后,随着时间的推移,您可以重新开始并升级调用旧方法的代码。这里的区别在于,在构建过程中您会看到“警告”,以帮助您找到所有需要升级的代码。

6

我不确定你在用什么语言,但是在。你可以创建编译器警告,这将允许你离开旧的引用一段时间,以便他们将按预期运行,但为其他开发人员看到警告。

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

+0

真棒小费。我希望我能投票两次。 – 2009-02-24 21:51:20

+0

接受这个答案是15点不是吗?几乎一样好! ;) – 2009-02-24 21:52:16

3

保持老方法到位,并转发到新的方法(如其他人所说的),而且在转发方法来发送日志消息,提醒自己将其取出。

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

1

有几个人就重构本身的编排提供了很好的答案。这很关键。但你也问过关于协调多人之间的变化(我认为这是你问题的关键)。你使用什么源代码控制?任何像CVS,SVN等都可以同时处理来自多个开发人员的传入更改。让它顺利进行的关键在于每个人都必须使自己的承诺具有粒度和原子性,并且每个开发人员都应该经常牵扯其他人的承诺。

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

我不确定把它分开,只是让它公开的部分是更好的。这违反了demeter的规律,并可能导致NullReference疼痛。

我建议将计时员暴露给没有涉及怪物的人。

如果您有任何分析API并看到可以在怪物内切割和封装的东西,当然,给怪物玩具玩,而不是让怪物做所有的工作本身,这是一个很好的召唤。主要的努力是定义玩具怪物需要简化他的工作。

+0

另一方面,这可能是一个起点 - 让它功能分开,然后在以后的日期去除怪物类。 – TofuBeer 2009-02-24 22:09:37

4

在分支中开发您的更改。将代码的一个子集分解为一个新类,在整个客户端进行更改,彻底测试,然后再合并。

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

将此结合Patrick的建议have the monster call the small monsters。如果您的合并客户端代码中断了该客户端的更改,那么可以轻松恢复。正如帕特里克所说,一旦证明没有人使用它,你就可以移除怪物的方法(现在存根)。

我也回应几个海报的建议,直接暴露破碎的类 - 而不是通过怪物。为什么只应用一半治疗?用同样的努力,你可以申请一个完整的治疗。

最后:编写单元测试。写很多单元测试。噢,小子,你需要单元测试来安全地把这个关掉。我有没有提到你需要单元测试?

-2

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

1

我会先看一下使用部分类将单个怪物类拆分为多个文件,将方法归类为类别。

在分割文件时,您需要停止任何人编辑怪物类。

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