Tuesday, July 23, 2013

Factories for Dummies

Today I was working on swapping a single threaded implementation of a piece of code to multithreaded version. Apart from concurrency issues, one exercise I've gone through was to use factories. Factories naturally facilitate the way we inject dependencies into our business logic. In order to make the previous statement more clear, lets go back to our awesome calculator service. It exposes an 'add' API. Let's start with a bad abstraction,

public class AwesomeCalculator {

    public long add(long a, long b) {
        Adder adder = new SimpleAdder();
        return adder.add(a, b);
    }
}

public Interface Adder {
    long add(long a, long b);
}

public SimpleAdder implements Adder {

    public long add(long a, long b) {
        //do something simple and stupid
    }
}

The client would do something like,

public class AwesomeCalculatorClient {
    private final AwesomeCalculator calculator = new AwesomeCalculator();

    public void foo() {
        calculator.add(1l, 2l);
    }
}

Now lets say we've got enlightenment and want to make our AwesomeCalculator use a better Adder implementation. The first place we've to update is the step inside add() method in AwesomeCalculator. The programmer who wants to do this change would want us to rot in hell for the poor design. It is just a one line change in this example, but think about a real piece of software where we'd end up changing all over the business logic wherever we've had assumptions about SimpleAdder. More importantly, it is almost impossible to mock out Adder in our unit tests.This is a total nightmare. Lets call our friend 'factory' to the rescue.

A decent version of our AwesomeCalculator would look something like,

public class AwesomeCalculator {

    private final AdderFactory adderFactory;

    public AwesomeCalculator(AdderFactory factory) {
        this.adderFactory = factory;         
    }

    public long add(long a, long b) {
        Adder adder = adderFactory.getAdder();
        return adder.add(a, b);
    }
}

public class AdderFactory {

    public Adder getAdder() {
        return new BetterAdder();
    }
}

With this version, we could modify the factory to produce a better Adder implementaion thus moving the change out of our AwesomeCalculator. To make the code more pretty, we can go one step further. Lets try one more revision.

public interface AdderFactory {
    public Adder getAdder();
}

public class StupidAdderFactory implements AdderFactory {

    @Override
    public Adder getAdder() {
        return new StupidAdder();
    }
}

public class BetterAdderFactory implements AdderFactory {

    @Override
    public Adder getAdder() {
        return new BetterAdder();
    }
}

This is even better, since we've completely moved the change to the client side. If the client wants a new implementation of Adder to be used, we just need to create a new factory which is capable of generating that Adder and let the client pass the new factory to AwesomeCalculator. Sweet isn't it ?

What have we done ?
  • Separated the concerns nicely.
  • Made the code constructor injectible. This will be super handy if we use framework like Spring which does inversion of control.
  • We can write clean unit tests by easily mocking out dependencies.
Ciao,
Surya.

No comments:

Post a Comment