Sunday, May 3, 2009

Dependency Injection

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.

No comments:

Post a Comment