Do we need to use Dependency injection pattern?
Dependency injection pattern becomes popular recent years. But what is the root cause of this event and do you really need to use this pattern in your work?
I know this pattern when my friend introduce me the Pico container and the article written by Martin Fowler (you can read it here). In the first read, I am impressed with what Martin said and what I did immediately after that is trying the frameworks is discussed in his article. Two years later, it is time for me to write some notes about this pattern after several real practices of applying it to my work.
Let's go with me and the simple example that all you know as the common first program "Hello world". I will illustrate ideas via Java program languages but it is also applied for any OO languages, especially for .NET.
Example 1: Hello world program
public class Helloworld { public static void main(string[] args) { System.out.println(“Hello world”); } }
This program is simple, right? And it does not represent any OO idea. But when the requirement changes that you need support the ‘Hello world’ program for multiple languages. The natural approach is separating class HelloWorld to separated classes.
Example 2: Re-writing 'Hello world' program
This program is very simple, right? And it does not represent any OO idea in source code. But when the requirement changes, it says that you need support the "Hello World" program for multiple languages. The natural approach is separating the class HelloWorld into separated classes.
public interface HelloWorld { public void sayHello(); } public class HelloworldEng implements Helloworld { public void sayHello() { System.out.println("Hello world"); } } public class TestProgram { public static void main(String[] args) { HelloWorld helloworld = new HelloWordEng(); helloworld.sayHello(); } }
Such program is better than the original but if user want to change the 'Hello world' message to another language we must modify the source code of TestProgram.java. It is violated with 'Open Closed principle'. What should we do now?
In fact to reduce the coupling between TestProgram and HelloWorld classes, we have two approaches. One is using Abstract Factory pattern and one is using Dependency Injection framework. If we use the first approach, the program is written like this
public class TestProgram { public static void main(string[] args) { Helloworld hello = HelloWordFactory.createHelloInstance(); hello.sayHello(); } }
public class HelloworldFactory { public Helloworld createHelloInstance() { //using reflection to load the Helloworld instance via //application property String className = System.getProperty("helloworld.instance"); Class c = Class.forName(className); .. } }
Using abstract factory method, whenever you need to change the implementation of Helloworld instance, you only need to modify the application property to the Helloworld implementation class name. This makes the TestProgram and Helloworld implementation are not relative and we do not violate the principle Open Closed.Now, let us return the second approach of using Dependency Injection (DI) pattern. We have 3 forms of DI, they are constructor, setter and interface. The example here using DI setter forms with Spring framework
Example 3: Using DI setter
public class HelloWorldService { private HelloWorld helloWorld; public void setHelloWorld(HelloWorld helloworldInst){ this.helloWorld = helloworldInst; } public void sayHello() { this.helloWorld.sayHello(); } }
After that, write the spring configuration and we can use the HelloWorldService in our TestProgram
public class TestProgram { public static void main(string[] args) { BeanFactory factory = new XmlBeanFactory( new ClassPathResource("HelloServiceEx.xml")); HelloWorldService service = factory.getBean("helloworldBean"); service.sayHello(); } }
Compare two approaches, we see that both of them achieve the same goal but DI framework like Spring has more advantages that it is not only the way of initializing the instance but more flexible and general when we set the pre-defined data for implementation instance. In simple application that only needs to care the way of initializing implementation instance, I suggest to use the Abstract Factory pattern but other cases using a DI framework is the prefer method. Using DI pattern can make us achieve the loose coupling among collaborating classes (the actor and implementation classes), it makes our program is more testable, debugable, flexible. It is all for a OO design.
![]()