Is Dependency Injection only useful for Unit Testing?
The rise in popularity of Dependency Injection over the last three years has been an interesting observation. “Lightweight containers” like Spring and Pico have really intrigued and excited the masses of Java Developers, so much so, that Spring (for example) is automatically adopted and accepted to be mandatory for almost all J2EE applications.
Looking back to when I was first reading about Spring, I clearly recall how the DI concepts and simple semantics of the XML configurations really got my creative juices flowing. I remember thinking about all the possibilities the framework could offer; specifically the view of configuring all my components so they had no knowledge of their dependents. Just after I created my “Hello World!” application, I quickly realized that the concepts of the Abstract Factory had just been reinvented, this time under the “Lightweight Container” marketing. Sure, the framework provides externalised definitions of object dependencies and promotes coding to interfaces, but its no different than my trusted old Abstract Factory pattern. Ignoring my cautious voice, the shiny marketing got the better of me and I convinced myself Spring could present a better alternative to my novel approaches; I’d decided to use it on my next project.
After about three months of using Spring in angst, I recall moments where I was enjoying the flexibility I’d attributed to Spring. Spring promoted the definition of loosely coupled Java classes which really gave me an advantage during unit testing. Heck, with a bit of thought, I could wire every one of my java classes using Spring bean definitions and define all my dependents as a reference to another Spring bean. Now, I can pass in Mocks, Stubs, basically anything that implemented the contract. What power! It gave me incredible leverage to test my primary test object by mocking or stubbing my fixtures or dependents; now, obtaining higher levels of code coverage was really easy. I just had to ensure that all the java classes that I needed to test were defined as a Spring bean, making sure that all the primary test objects dependents were also defined as bean references. This way, by overriding specific bean definitions with Mock or Stub ones, I could replace them at runtime to exercise every concern of my application code.
One observation that I made during this time was the reduced number of the
new operator usage. Looking though my source, everything was injected into my classes and the
new operator had practically vanished. I recall the infamous “Dont call me, we’ll call you” cliche had whispered in my mind.
Looking back over my first Spring project, I’d say we achieved a test code coverage of about 80%. This was really exciting and I’d give all the credit to our decision to use Spring. A few years later, a few more gray hairs and a few more Spring enabled projects, I’ve realised that Spring is only an enabler to achieve high unit test code coverage. Sure, Spring privides some really nice JDBC, JNDI, JMS etc helpers, but the XML configurations aspect that it gives to an application can get really messy; so much so that it can become a real development impedance. How is this possible? Read my Aspects of Spring on a Monolithic Codebase blog; it sums up my growing pains.
So, the one resounding question that I have in my mind is “What real benefit does Spring offer to a system?”. After many discussions and hours of mental deliberation, I categorise the benefits of Spring by percentage as 90% unit testing flexibility and 10% utility.
Where to from here? Well, that depends on the importance you place on code coverage. Period. If your aiming for high levels of code coverage, then Spring’s your answer; just expect to deal with a pervasive XML configuration aspect of your codebase.
Proposing alternatives to Spring could be futile given the wild acceptance of the framework. In my humble view, Abstract Factories are formidable alternatives and can also be configuration driven and, again, cause a proliferation of dependency descriptor externalisation, however, they force you to think about your providers and give you a break from the Spring alternative to think about when you need to abstract your providers. This way, your decisions to define your configurable components can be made with more pragmatism and care.
In conclusion, using Spring has many advantages, most of which are, sadly, concerned with the testability requirements of your codebase. Separating your application tiers by demarcating with a layer of Spring configuration will be my next approach. This way, I can achieve my provider view using the attractive semantics of Spring bean definitions.