Domanda

Our team has been given a legacy system for further maintenance and development. As this this is true "legacy" stuff, there is really, really small number of tests, and most of them are crap. This is an app with web interface, so there are both container-managed components as well as plain java classes (not tied to any framework etc.) which are "new-ed" here and there, whenever you want to.

As we work with this system, every time we touch given part we try to break all that stuff into smaller pieces, discover and refactor dependencies, push dependencies instead of pulling them in code.

My question is how to work with such system, to break dependecies, make code more testable etc? When to stop and how to deal with this?

Let me show you an example:

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
}

This constructor definitely does too much. Also to test this for further refactoring it requires doing magic with PowerMock etc. What I'd do is to change it into:

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

or provide constructor setting zone only with setters for dependencies.

The problem I see is that if I provide dependencies in constructor, I still need to provide them somewhere. So it is just moving problem one level up. I know I can create factory for wiring all the dependencies, but touching different part of app will cause having different factories for each. I obviously can't refactor all the app at once and introduce e.g. Spring there.

Exposing setters (maybe with default implementations provided) is similar, moreover it is like adding code for tests only.

So my question is how you deal with that? How to make dependencies between objects better, more readable, testable without doing it in one go?

È stato utile?

Soluzione

I'd try to establish a rule like the boy scout rule: When ever you touch a file you have to improve it a little, apart from implementing what ever you wanted to implement.

In order to support that you can

  • agree on a fixed time budget for such improvements like for 2 hours of feature work we allow 1 hour of clean up.

  • Have metrics visible showing the improvement over time. Often simple things like average file size and test coverage are sufficient

  • Have a list of things you want to change at least for the bigger stuff, like "Get rid of TelSystemConfiguratorFactory" track on which tasks you are already working and prefere working on things that already started over new things.

In any case make sure management agrees to your approach.

On the more technical side: The approach you showed is is good. In many cases I would consider a second constructor providing all the dependencies but through the new constructor with the parameters. Make the additional constructor deprecated. When you touch clients of that class make them use the new constructor.

If you are going with Spring (or some other DI Framework) you can start by replacing calls to static Factories with getting an instance from the Spring context as an intermediate step before actually creating it via spring and injecting all the dependencies.

Altri suggerimenti

I just recently started reading "Working effectively with legacy code" by Michael Feathers. The book is basically an answer to your very question. It presents very actionable "nuggets" and techniques to incrementally bring a legacy system under test and progressively improve the code base.

The navigation can be a little confusing, as the book references itself by pointing to specific techniques, almost from page 1, but I find that the content is very useful so far.

I am not affiliated with the author or anything like that, it's just that I am facing similar situations and found this a very interesting resource.

HTH

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top