Sunday, September 7, 2008

Getting rid of dependencies

I recently moved to another job where I have to support a legacy system now. That is fine by me since after a while any project becomes legacy (unless you write unit tests of course :)) and there are also certain advantages in supporting legacy code. The downside is that at this place a common attitude to unit testing is “unit tests don't work for us”. As a consequence it's not possible to run the project without database which is located in another part of the world and a messaging system being connected to some other quite complicated piece of software. What a great opportunity to develop dependency removing skills ;)

The basic idea of replacing dependencies in OOP language is to add some level of indirection using polymorphism. The simplest way I can think of how to do it is to force all “evil” dependencies go through some class with methods which you can override. So in production mode application will use all the dependencies as it did before, and for testing or development mode you will be able to override methods on this class to do whatever you want without using real code. This idea is somewhat similar to “Skin and wrap the API” and “Responsibility-Based Extraction” described in the Working Effectively with Legacy Code book by Mike Feathers. The difference as I see it is that adding “dependency redirection facade” is faster and more dirty move which can help to extinguish evil dependencies with minimum efforts. Probably, the next move after “dependency redirection facade” should be one of those Mike Feathers suggests. Here is a contrived example:

public class User {
    public void refreshColumn() {
        String update = DataProcessor.getInstance().retrieveUpdate();
        List<Data> dataList = DBUtils.getData();
        for (Data data : dataList) {
            Data updatedData = DataProcessor.getInstance().applyUpdate(data, update);
            // ...
        }
        // some useful code
    }
}

In thisIn this example DataProcessor obtains an update which is then applied to the data coming from some database with the help of the DBUtils class. DataProcessor here is a singleton and DBUtils can be any kind of class, the only thing that matters is that it has static method getData() which is invoked by user code. The first step to get rid of evil dependencies is to move all these calls into a separate class:

public class RedirectionFacade ...
    public List<Data> getData() {
        return DBUtils.getData();
    }

    public String retrieveUpdate() {
        return DataProcessor.getInstance().retrieveUpdate();
    }

    public Data applyUpdate(final Data data, final String update) {
        return DataProcessor.getInstance().applyUpdate(data, update);
    }
Then we should make it possible to subclass RedirectionFacade and use it subclass instance instead of RedirectinFacade without any changes in user code. It can be done like this:
public class RedirectionFacade ...
    private static RedirectionFacade instance = new RedirectionFacade();

    public static void setInstance(final RedirectionFacade instance) {
        RedirectionFacade.instance = instance;
    }

    public static RedirectionFacade getInstance() {
        return instance;
    }

Here RedirectionFacade is a kind of singleton with a setter (it makes it not a singleton, though) or it may be it's better to think of getInstance() as a factory method which returns whatever we put in with setInstance().

After the above changes user code will work as it did before and will look like this:

public class User {
    public void refreshColumn() {
        String update = RedirectionFacade.getInstance().retrieveUpdate();
        List<Data> dataList = RedirectionFacade.getInstance().getData();
        for (Data data : dataList) {
            Data updatedData = RedirectionFacade.getInstance().applyUpdate(data, update);
            // ...
        }
        // some useful code
    }
}
The benefit of all the above is that now it's possible to subclass RedirectionFacade and put whatever implementation we want. For example to instantiate User class without evil dependencies we can create implementation of RedirectionFacade like this:
public class DummyRedirectionFacade extends RedirectionFacade {
    @Override public List<Data> getData() {
        return new ArrayList<Data>();
    }

    @Override public String retrieveUpdate() {
        return "";
    }

    @Override public Data applyUpdate(final Data data, final String update) {
        return data;
    }
}
And a testcase for the User class might look like this:
public class UserTest {
    @Test
    public void testUserShouldRefreshColumn() {
        // setup
        RedirectionFacade.setInstance(new DummyRedirectionFacade());
        final User user = new User();
        // exercise
        user.refreshColumn();
        // verify
        // ...
    }
}

The pros of the above approach is that:

  • it is the simplest approach so it can be applied pretty fast;
  • works for sure even for static methods.

The cons of the above approach is that:

  • can get messy with lots of dependencies;
  • requires a lot of simple changes in user code (shouldn't be a big deal if your team is doing TDD).

0 comments: