I've been reading up on the Factory pattern, and have come across articles that suggest using the Factory pattern in conjunction with dependency injection to maximize reusability and testability. Although I have not been able to find any concrete examples of this Factory-DI hybrid, I'm going to try and give some code examples of my interpretation. However, my question is really about how this approach improves testability.
My interpretation:
So we have a Widget
class:
public class Widget {
// blah
}
And we want to include a WidgetFactory
to control the construction of Widget
s:
public interface WidgetFactory {
public abstract static Widget getWidget();
}
public class StandardWidgetFactory implements WidgetFactory {
@Override
public final static Widget getWidget() {
// Creates normal Widgets
}
}
public class TestWidgetFactory implements WidgetFactory {
@Override
public final static Widget getWidget() {
// Creates test/mock Widgets for unit testing purposes
}
}
Although this example uses Spring DI (that's the only API I have experience with), it doesn't really matter if we're talking about Guice or any other IoC framework; the idea here is that we're now going to inject the correct WidgetFactory
implementation at runtime to depending on whether we are testing the code or running normally. In Spring the beans config might look like this:
<bean id="widget-factory" class="org.me.myproject.StandardWidgetFactory"/>
<bean id="test-widget-factory" class="org.me.myproject.TestWidgetFactory"/>
<bean id="injected-factory" ref="${valueWillBeStdOrTestDependingOnEnvProp}"/>
Then, in the code:
WidgetFactory wf = applicationContext.getBean("injected-factory");
Widget w = wf.getWidget();
This way, an environmental (deployment-level) variable, perhaps defined in a .properties file somewhere, decides whether Spring DI will inject a StandardWidgetFactory
or a TestWidgetFactory
.
Am I doing it right?!? This seems like an awful lot of infrastructure just obtain good testability for my Widget
. Not that I'm opposed to it, but it just feels like over-engineering to me.
My hangup:
The reason why I am asking this is because I will have other objects in other packages, which have methods that use Widget
objects inside of them. Perhaps something like:
public class Fizz {
public void doSomething() {
WidgetFactory wf = applicationContext.getBean("injected-factory");
Widget widget = wf.getWidget();
int foo = widget.calculatePremable(this.rippleFactor);
doSomethingElse(foo);
}
}
Without this huge, seemingly-over-engineered setup, there would be no way for me to inject "mock Widgets
" into my unit test for Fizz::doSomething()
.
So I'm torn: on one end it just feels like I'm overthinking things - which I may very well be doing (if my interpretation is incorrect). On the other hand I don't see any clean way to get around it.
As a segue into a tangential question, this also raises another huge concern of mine: if my interpretation is correct (or even somewhat correct), then does this mean we need Factories
for every object?!?
That sounds like way-overengineering! What's the cut-off? What's the line in the sand that demarcates when to use a Factory, and when not to?! Thanks for any help and apologies for a verbose question. It's just got my head spinning.