如何逐步从大型系统中的遗留物中恢复?

时间:2012-04-16 19:15:34

标签: java dependency-injection refactoring legacy-code code-cleanup

我们的团队获得了进一步维护和开发的遗留系统。 因为这是真正的“传统”东西,实际上,真的是非常少量的测试,而且大多数都是垃圾。这是一个带有Web界面的应用程序,所以有容器管理的组件以及普通的java类(没有绑定到任何框架等),无论何时你想在哪里“new-ed”。

当我们使用这个系统时,每次触摸给定的部分时,我们都会尝试将所有内容分解为更小的部分,发现并重构依赖项,推送依赖项而不是将它们拉入代码中。

我的问题是如何使用这样的系统,打破家属,使代码更可测试等?何时停止以及如何处理?

让我举个例子:

public class BillingSettingsAction {

    private TelSystemConfigurator configurator;
    private OperatorIdDao dao;

    public BillingSettingsAction(String zoneId) {
        configurator = TelSystemConfiguratorFactory.instance().getConfigurator(zoneId);
        dao = IdDaoFactory.getDao();
        ...
    }

    // methods using configurator and dao
}

这个构造函数肯定做得太多了。另外为了进一步重构测试它需要用PowerMock等做一些魔术。我要做的就是把它改成:

public BillingSettingsAction(String zone, TelSystemConfigurator configurator, OperatorIdDao dao) {
    this.configurator = configurator;
    this.dao = dao;
    this.zone = zone;
}

或仅为依赖项的setter提供构造函数设置区域。

我看到的问题是,如果我在构造函数中提供依赖项,我仍然需要在某处提供它们。所以它只是将问题提升一级。我知道我可以创建工厂来连接所有依赖项,但触摸应用程序的不同部分将导致每个工厂有不同的工厂。我显然无法立即重构所有应用程序并引入,例如那里的春天。

公开setter(可能提供默认实现)是类似的,而且它就像添加代码仅用于测试。

所以我的问题是你如何处理?如何使对象之间的依赖关系更好,更易读,更可测试而无需一次性完成?

2 个答案:

答案 0 :(得分:3)

我刚刚开始阅读Michael Feathers的“有效使用遗留代码”。 这本书基本上是你的问题的答案。它提供了非常可操作的“小块”和技术,可以逐步使遗留系统得到测试,并逐步改进代码库。

导航可能有点令人困惑,因为本书通过指向特定技术来引用自身,几乎从第1页开始,但我发现内容到目前为止非常有用。

我与作者或类似的东西没有任何关系,只是我面对类似的情况,发现这是一个非常有趣的资源。

HTH

答案 1 :(得分:2)

我试图建立一个像童子军规则这样的规则:当你触摸文件时,你必须稍微改进它,除了实现你想要实现的任何东西。

为了支持你可以

  • 就这些改进达成一致的固定时间预算,例如2小时的特色工作,我们允许1小时的清理。

  • 有可见的指标显示随时间的改善。通常很简单的事情,如平均文件大小和测试覆盖率就足够了

  • 列出你想要更改的内容,至少是为了更大的内容,例如“摆脱TelSystemConfiguratorFactory”跟踪你已经在哪些任务上工作,并优先处理已经开始的新事物。< / p>

无论如何,请确保管理层同意您的方法。

在更技术方面:你展示的方法是好的。在许多情况下,我会考虑第二个构造函数提供所有依赖项,但通过带有参数的新构造函数。不建议使用其他构造函数。当您触摸该类的客户端时,请使用新的构造函数。

如果你要使用Spring(或其他一些DI框架),你可以先从Spring上下文中获取一个实例作为中间步骤来替换对static Factories的调用,然后再通过spring实现它并注入所有依赖项。< / p>