我目前正在编写一段代码,其中逻辑和数据访问都存在于 GUI 类中。显然,我想改善这种情况。
目前的结构基本上是:
- 大泥球
最终目标是实现类似 DDD 的结构:
- 达尔
- 领域模型
- 服务层
- 演示模型
- 图形用户界面
那么,你将如何解决这个问题呢?
- 大爆炸
- 定义最终状态的结构并将代码推送到其最终位置。
- 分而治之
- 试着把大泥球分成两块。重复直到完成...
- 扼杀
我目前正在编写一段代码,其中逻辑和数据访问都存在于 GUI 类中。显然,我想改善这种情况。
目前的结构基本上是:
最终目标是实现类似 DDD 的结构:
那么,你将如何解决这个问题呢?
永远不要尝试“大爆炸”。它几乎总是扑面而来,因为当其他一切都失败时,这是一种高风险、绝望的措施。
分而治之:这很好用……如果你的世界只有两个方面。在真正的软件中,你必须同时征服如此多的战线,你很少能负担得起生活在黑白幻想中的代价。
我想在我职业生涯的大部分时间里,我一直在使用“扼杀”之类的东西:逐渐将糟糕的旧代码转变为闪亮的新代码。这是我的食谱:
从某个地方开始,从哪里开始并不重要。编写一些单元测试以查看代码的实际行为。找出它做你认为它做的事情的频率,以及它不做的频率。使用您的 IDE 重构代码,以便您可以对其进行测试。
第一天之后,猜猜你是否从正确的地方开始拆解这个怪物。如果是这样,继续。如果没有,找一个新的地方重新开始。
这种策略的优点:它以小步骤工作,因此可以控制风险,如果出现问题,是否必须在您上周工作的代码中。
缺点:这需要很多时间,而且你会感到沮丧,因为通常情况下,进展似乎很慢,直到“结”弹出,突然间,一切都开始到位,就像施了魔法一样。
我从来没有听说过“扼杀者应用程序”这个词——我喜欢它。在可能的情况下,这始终是一个好方法,它无疑将风险降到最低,并且非常务实,将这座大大厦一块块地削掉。
根据我的经验,这不起作用的地方是需要立即进行相当大的更改 - 需要进行一些重构(或大量黑客攻击)的更改。在那种情况下,我经常发现我需要做的改变就在大泥球的中心,除了变脏别无选择——即使应该是标准维护或微小的增强改变也太可怕了,而且主要重构是最好的选择。
对于这些情况,我会选择分而治之——我一直瞄准的第一个目标是可测试性,一旦你有了它,剩下的就容易多了。事实上,这通常是我从大泥球中重构的主要驱动力之一——那种代码通常几乎是不可测试的,希望有示例 UI 输入和输出,但有时甚至缺少.
因此,当面对所有内容都集中在 UI 中的代码时,我通常首先将离散的功能单元分解为类和方法,然后将这些代码部分下推到域或服务层中。一点一点地做这件事大大减少了破坏某些东西的机会,并且当事情出错时更容易查明破坏代码的位置。
在每次更改结束时运行您可用的任何测试用例,并确保您仍然满足某种基线。
如果你写好的单元测试,你可以开始减少问题的规模,我发现采用扼杀方法很快就会变得实用——使用体面的单元测试或至少有正确的框架来允许编写体面的单元测试逐渐替换部分功能变得更加实用。
我偶然发现了似乎很有希望解决此类问题的“天皇方法”。
http://mikadommethod.wordpress.com/
Øredev 2010 中也有关于 Mikado 方法的讨论。
http://oredev.org/2010/sessions/large-scale-refactorings-using-the-mikado-method
Big bang / Big re-design / rewriting the SW ... 或任何其他名称都不适用于生活 SW。原因是:
您仍然需要使用(可能)您拥有的相同资源来支持现有的软件。
你可能没有重写的要求。您的旧代码中嵌入了所有要求。你们的工程师都不知道所有的软件领域和所有的要求。
Rewriting will take time. At the end of this time you will find that the existing SW has changed to support things that were required during this time. your new SW actually split from the original and merge will be needed (which will also take time).
取决于您是否必须始终保持工作状态,以便您可以随时进行错误修复和部署,那么Devide and Conquer将是一个很好的解决方案。如果您可以维护旧代码,同时处理新代码(并有纪律将错误修复应用于两个代码库),那么重写可能是更好的解决方案。
如果通过重构,您的意思是在不修改功能的情况下改进代码,我将从创建一个自动化回归测试基线开始。有很多工具可以帮助解决这个问题。我使用TestComlete虽然有很好的廉价替代品。
建立回归测试基线后,我个人会选择分而治之,因为根据我的经验,它最有可能成功。一旦你有了一个测试基线,你选择哪种方法就不再重要了。
对我来说,这取决于情况。
如果这是一个非常小的项目,我会很想从头开始重写它……但是您通常不会拥有这种奢侈。
做不到这一点,我会去一块一块地切掉它。我会编写单元测试来验证现有功能,并慢慢使用 TDD 将代码转换为优雅且设计良好的系统。根据这个过程需要多长时间,它可能开始看起来像你上面提到的 StranglerApplication。
BigBang 风险很大,因为您没有简单的方法来验证更新后的系统是否与旧系统执行相同的操作。
分而治之的风险比 BigBang 的风险小……但如果它的系统足够大,它最终可能会像 BigBang 一样有问题。
完全重写是一种选择吗?根据我的经验,从头开始重写通常比试图清理现有的混乱更有效。您仍然保留部分现有代码,但在新的上下文中。如果你有 gui 和数据库,也是如此。从头开始重写并随身携带您可以使用的东西。
从一个干净的新架构开始,将旧的代码片段逐段移动到这个新架构中,并对其进行重构以适应新架构将是一个不错的选择。我认为在移动功能时采用自下而上的方法会很好。