<< Part 1 (Introduction)

 
Application of Inversion of Control (IoC) Pattern  - Part 2

The best way to describe what IoC is about, and what benefits it can provide, is to look at a simple example.

The following JDBCDataManger class is used to manage our application's accessing of the database. This application is currently using raw JDBC for persistence. To access the persistence store via JDBC, the JDBCDataManger will need a DataSource object. The standard approach would be to hard code this DataSource object into the class, like this:

public class JDBCDataManger {
 public void accessData() {
 DataSource dataSource = new DataSource();
 //access data
 ...
}

Given that JDBCDataManger is handling all data access for our application, hard coding the DataSource isn't that bad, but we may want to further abstract the DataSource, perhaps getting it via some system-wide property object:

public class JDBCDataManger {
 public void accessData() {
 DataSource dataSource = 
 ApplciationResources.getDataSource();
}

In either case, the JDBCDataManger has to fetch the DataSource itself.

IoC takes a different approach — with IoC, the JDBCDataManger would declare its need for a DataSource and have one provided to it by an IoC framework. This means that the component would no longer need to know how to get the dependency, resulting in cleaner, more focused, and more flexible code.

 

Interface Injection (Type 1)

With Interface Injection IoC, components implement specific interfaces provided by their containers in order to be configured.


 public class JDBCDataManger implements Serviceable {
 DataSource dataSource;
public void service (DataSource dataSource){
 this.dataSource = dataSource; 
 } 
 
public void getData() { //use dataSource for something }
}


public interface Serviceable { void service(DataSource dataSource); }

This form of IoC has been around for longer than the term IoC has been in use — many of you might have used
such a form of IoC when using the EJB framework, for example. Here, your components extend and implement specified interfaces, which then get called by the framework itself.

Avalon is an example of a framework that uses this technique in places.The fact that the Avalon framework has been providing an IoC framework for several years now, without generating nearly as much interest in the idea as either Spring or PicoContainer, is probably due to the downsides of this approach.

The requirement to implement specific interfaces can give code a "bloated" feel, while at the same time coupling your application code to the underlying framework. The benefits provided by the other two forms of IoC we will look at next far outweigh those provided by this form of IoC.

 

Setter Injection (Type 2)

With Setter Injection IoC, some external metadata is used to resolve dependencies. In Spring, this metadata takes the form of an XML configuration file. With this form of IoC, the JDBCDataManager class looks like a normal bean:

public class JDBCDataManger {
 private DataSource dataSource;
public void setDataManager(DataSource dataSource { this.dataSource = dataSource; } public void getData() { //use dataSource for something } }

Our JDBCDataManger component exposes its dataSource property to allow Spring to set it. Spring does this using its XML configuration. First we define a data source bean (which can be reused by multiple components):

<bean id="myDataSource" 
 class="org.apache.commons.dbcp.BasicDataSource" >
 <property name="driverClassName">
 <value>com.mydb.jdbc.Driver</value>
 </property>
 <property name="url">
 <value>jdbc:mydb://server:port/mydb</value>
 </property>
 <property name="username">
 <value>root</value>
 </property>
</bean>

Next, we define an instance of our manager and pass in a reference to the data source:

<bean id="dataManager" 
 class="example.JDBCDataManger">
 <property name="dataSource"> 
 <ref bean="myDataSource"/> 
 </property> 
</bean> 

At runtime, a JDBCDataManger class will be instantiated with the correct DataSource dependency resolved, and we will be able to access the bean via the Spring framework itself.

The definition of dependencies in this way makes unit testing a breeze: simply define an XML file for your mock objects, replacing your normal XML file, and away you go.

Perhaps the main advantage of Setter Injection is that application code is not tied to the container in any way, but this is also a downside — it's not immediately clear how this JDBCDataManger component relates to everything else. It almost seems as though the DataSource is being magically passed to the JDBCDataManger, as the dependency management is being done outside of the Java code. Another disadvantage is that Spring requires getters and setters for its dependency resolution. You have to expose properties that you might perhaps not otherwise expose, potentially breaking data encapsulation rules and, at best, making a class' interface more complex than is needed.

With Spring's metadata being described in XML, it cannot be validated at compile time during normal Java compilation, meaning issues with broken dependencies can only be spotted at runtime.

 

Constructor Injection (Type 3)

Constructor Injection is based around this principle of the "Good Citizen." The Good Citizen pattern was introduced by Joshua Bloch, to describe objects that, upon creation, are fully set up and valid to use. In practice, this means that objects should not need additional methods to be called on them after instantiation in order to make them usable. The result is that you can sure that once you've created such an object, it's fit to use. This radically simplifies your code and reduces the need for defensive coding checks, while at the same time making your code more defensive as a whole. Such code is also very easy to test.

With Constructor Injection, you register an object with the framework, specify the parameters to use (which can in turn be created by the framework itself) and then just request an instance. The components being registered just have to implement a constructor, which can be used to inject the dependencies. Recently, Spring has introduced support for this form of IoC, but we'll look instead at PicoContainer, which has been built around this principle. Let's look at our JDBCDataManger, now recoded for use with a Constructor Injection framework:

public class JDBCDataManger {
 private DataSource dataSource;
public JDBCDataManger(DataSource dataSource) { this.dataSource = dataSource; } public void getData() { //use dataSource for something } }

Like Setter Injection IoC, the application code is independent of the framework itself, and also gives you the advantages inherited from the use of the Good Citizen pattern. In addition, given that Container only requires a constructor, we have to make much less provisioning for the use of an IoC framework than with Setter Injection IoC.

Potential downsides with this approach are that using constructors to maintain the dependencies can become more complex when using inheritance, and it can cause issues if you use your constructors for purposes other than simply initializing the object. (Which some do!)

References:

- Hollywood Principle defined at Answers.com
- Inversion of Control defined at Answers.com
- Dependency Injection defined at Answers.com
- Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler
-A beginners guide to Dependency Injection (TheServerSide; July, 2005)
-
Examining the Validity of Inversion of Control (TheServerSide; Feb, 2005)
-
A Brief Introduction to IoC (Basic)
- Design Better Software with IOC (Advanced)

<< Back (Part 1)