What is Dependency Injection?

Connect with me:  
  Jenkov.com - News

Dependency Injection (DI) has been a hot topic the last two-three years. Lots have already been said and written about depencency injection, yet I feel there is more to say. This trail will explain and explore dependency injection and related concepts. Below is a list of the topics covered in this trail. The list is repeated at the top right of every page in this trail.

  Dependency Injection
What is Dependency Injection?
Dependency Injection Containers
Butterfly Container
Dependency Injection Benefits
When to use Dependency Injection
Is Dependency Injection Replacing the Factory Patterns?
The Opportunities Missed by Annotation Based Dependency Injection
 
Design of a Dependency Injection (DI) Container
Butterfly Container Script
- Design Considerations
Butterfly DI Container
- Internal Design
Butterfly DI Container
- Global and Local Factories
 

Dependency injection is an expression coined in Martin Fowler's article Inversion of Control Containers and the Dependency Injection Pattern. This is an nice article, but it misses some of the benefits of dependency injection containers. Therefore I also disagree with the articles conclusion, but more on that in a different text.

Dependency injection is a style of object configuration in which an objects fields and collaborators are set by an external entity. In other words objects are configured by an external entity. Dependency injection is an alternative to having the object configure itself. This may sound a bit abstract so let's look at a simple example:

  public class MyDao {

    protected DataSource dataSource =
    new DataSourceImpl("driver", "url", "user", "password");

    //data access methods...
    public Person readPerson(int primaryKey) {...}

  }

This DAO (Data Access Object) class, MyDao, needs a javax.sql.DataSource instance in order to obtain database connections. The database connections are used to read from and write data to the database, for instance Person objects.

Notice how the MyDao class instantiates a DataSourceImpl instance as its needed DataSource. The fact that the MyDao class needs a DataSource implemenation means that it "depends" on it. It cannot carry out its work without a DataSource implementation. Therefore, MyDao has a "dependency" on the DataSource interface and on some implementation of it.

The MyDao class itself instantiates a DataSourceImpl as its DataSource implementation. Therefore the MyDao class is said to "satisfy its own dependencies". When a class satisfies its own dependencies it automatically also depends on the classes it satisfies the dependencies with. In this case MyDao now also depends on DataSourceImpl, and on the four hardcoded string values passed as parameter to the DataSourceImpl constructor. You cannot use a different value for the four strings, nor use a different implementation of the DataSource interface, without changing the code.

As you can see, when a class satisfies its own dependencies it becomes inflexible with regards to these dependencies. This is bad. This means, that if you need to change the dependencies you will have to change the code. In this example, if you need to use a different database, you will need to change the MyDao class. If you have many DAO classes implemented like this you will need to change them all. In addition, you cannot unit test the MyDao class using a mock DataSource implementation. You can only use the DataSourceImpl. It doesn't take much brains to figure out that this is a bad idea.

Let's change the design a little:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password){
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }


  //data access methods...
  public Person readPerson(int primaryKey) {...}

}

Notice how the DataSourceImpl instantiation is moved into a constructor. The constructor takes four parameters which are the four values needed by the DataSourceImpl. Though the MyDao class still depends on these four values, it no longer satisfies these dependencies itself. They are provided by whatever class creating a MyDao instance. The values are said to be "injected" into the MyDao constructor. Hence the term "dependency injection". It is now possible to change the database driver, url, user name and password used by the MyDao class, without changing the MyDao class.

Dependency injection is not restricted to constructors. You can also inject dependencies using setter methods, or directly into public fields.

The MyDao class can still be made more independent. It still depends on both the DataSource interface and the DataSourceImpl class. There is no need for it to depend on more than the DataSource interface. This can be achieved by injecting a DataSource into the constructor instead of the four string parameters. Here is how that looks:

  public class MyDao {

    protected DataSource dataSource = null;
    
    public MyDao(DataSource dataSource){
      this.dataSource = dataSource;
    }

    
    //data access methods...
    public Person readPerson(int primaryKey) {...}

  }

Now the MyDao class no longer depends on the DataSourceImpl class, or the four strings needed by the DataSourceImpl constructor. You can now inject any DataSource implementation into the MyDao constructor.

Dependency Injection Chaining

The example in the previous section is a bit simplified, and doesn't do dependency injection full justice. You may argue that the dependency is now moved from the MyDao class to each client using the MyDao class. Clients now have to know about some DataSource implementation to be able to inject it into the MyDao constructor. Here is an example:

  public class MyBizComponent{
    public void changePersonStatus(Person person, String status){

       MyDao dao = new MyDao(
            new DataSourceImpl("driver", "url", "user", "password"));

       Person person = dao.readPerson(person.getId());
       person.setStatus(status);
       dao.update(person);
    }
  }

As you can see the MyBizComponent now depends on the DataSourceImpl class and the four strings needed by its constructor. This is even worse than having the MyDao depend on them because the MyBizComponent now depends on classes and information that it isn't using itself. Furthermore the DataSourceImpl and constructor parameter belongs to a different abstraction layer. A layer below the MyBizComponent - the DAO layer.

The solution is to continue the dependency injection all the way up through the layers. The MyBizComponent should only depend on a MyDao instance. Here is how that looks:

  public class MyBizComponent{

    protected MyDao dao = null;

    public MyBizComponent(MyDao dao){
       this.dao = dao;
    }
    

    public void changePersonStatus(Person person, String status){
       Person person = dao.readPerson(person.getId());
       person.setStatus(status);
       dao.update(person);
    }
  }

Again the dependency, MyDao, is injected into the class via the constructor. Now the MyBizComponent only depends on the MyDao class. If MyDao was an interface you could even switch implementation without the MyBizComponent knowing about it.

This dependency injection pattern is continued all the way up the layers of your application, from the lowest data accessing layers up to the user interface (if any).


NextNext : Dependency Injection Containers

Connect with me:
     
Newsletter - Get all my free tips!