Monday, August 01, 2005
Light Weight Containers - Inversion Of Control (IoC) or Dependency Injection (DI) Frameworks
Last few years have seen a huge interest in lightweight frameworks in enterprise Java world to counter the complexity of heavyweight containers such as EJB. I have been reading a lot of people rave about IoC and how it makes life of a developer easier. While reading EJB3 first public draft spec, I saw references to dependency injection, this thing has arrived and I had no clue as to what it is. So I decided to get a feel for IoC. These are my findings so far.
IoC Defined
Objects in a system are associated with each other and they collaborate to provide a piece of business functionality. The objects depend on each other. Traditionally the task of resolving object dependency and setting up object collaborators is required programmatically before executing the business logic. A lot of code is required to wire up all the dependent objects. In J2EE world this often translates to performing JNDI lookup via a service locator to find the objects and instantiating them and setting up appropriate attribute in the business object. IoC frameworks invert the control with respect to resolution, creation and setting up of collaborators when performing a task or piece of business functionality or use case. For example in a customer relationship management system, the act of creating a customer may involve creating the customer object itself, creating customer contacts and creating products the customer has purchased. A customer service implemented as a session façade will need collaborator objects: Customer Business Object (Customer BO), Contact BO and Product BO. These business objects can be POJOs or EJBs (stateless that use DAO or entity EJBs for persistence). In a non IoC environment the customer service façade will need to find/locate the collaborators (Customer BO, Contact BO and Product BO), create them and setup the appropriate attributes before implementing business function. In an IoC environment, on the other hand , one simply declares the dependency (using a mechanism provided by the IoC framework) on the collaborators of customer service façade. At runtime the IoC framework ensures that these collaborators are wired up (again via an IoC framework specific mechanism) and ready to use before a customer business operation is called on it (say create). Its obvious from the description that instead of customer service façade controlling the collaborators, something else is controlling it - the IoC framework - hence the name Inversion of Control.
Any IoC framework has following characteristics (from Mike Spille's Blog):
Types of DI
There are 3 types of IoC frameworks. These are known, rather unimaginatively, as type 1, type 2 and type 3 based on how collaborators are setup i.e. dependency is injected. Martin Fowler gave them a more meaningful name:
Essential Tensions
Constructor Vs Setter
Constructor-based injection wires up the dependencies at object construction time. This gives a valid object at creation. However if there are many dependencies, the constructor arguments can become large and cumbersome. The setter based construction is simpler, more intuitive and provides flexibility that might not be available with constructors.
Configuration file vs Java Code wiring
Configuration file gives more flexibility and the advantages are obvious. The dependencies can be modified just by modifying the config files without requiring a compile or build. The Java code wiring is useful in situations where component assembly is complex and requires logic (if then else) – in those scenarios expressing dependencies using config files becomes difficult. Only logical choice is a programming language with rich sematics for complex logic. Java code wiring is going to be simpler and easier. A dynamic language like Groovy can be really useful here since being dynamic it won’t require compilation whereas Java would.
Advantages
Popular DI/IoC Frameworks
IoC Defined
Objects in a system are associated with each other and they collaborate to provide a piece of business functionality. The objects depend on each other. Traditionally the task of resolving object dependency and setting up object collaborators is required programmatically before executing the business logic. A lot of code is required to wire up all the dependent objects. In J2EE world this often translates to performing JNDI lookup via a service locator to find the objects and instantiating them and setting up appropriate attribute in the business object. IoC frameworks invert the control with respect to resolution, creation and setting up of collaborators when performing a task or piece of business functionality or use case. For example in a customer relationship management system, the act of creating a customer may involve creating the customer object itself, creating customer contacts and creating products the customer has purchased. A customer service implemented as a session façade will need collaborator objects: Customer Business Object (Customer BO), Contact BO and Product BO. These business objects can be POJOs or EJBs (stateless that use DAO or entity EJBs for persistence). In a non IoC environment the customer service façade will need to find/locate the collaborators (Customer BO, Contact BO and Product BO), create them and setup the appropriate attributes before implementing business function. In an IoC environment, on the other hand , one simply declares the dependency (using a mechanism provided by the IoC framework) on the collaborators of customer service façade. At runtime the IoC framework ensures that these collaborators are wired up (again via an IoC framework specific mechanism) and ready to use before a customer business operation is called on it (say create). Its obvious from the description that instead of customer service façade controlling the collaborators, something else is controlling it - the IoC framework - hence the name Inversion of Control.
Any IoC framework has following characteristics (from Mike Spille's Blog):
- IoC framework controls the creation of objects.
- IoC framework manages the lifecycle of the objects.
- IoC framework resolves the dependencies between objects it manages.
Types of DI
There are 3 types of IoC frameworks. These are known, rather unimaginatively, as type 1, type 2 and type 3 based on how collaborators are setup i.e. dependency is injected. Martin Fowler gave them a more meaningful name:
- Constructor Based Injection (or Type 1): In this the constructor of the object declares as argument its collaborator (or dependencies). The IoC frameworks via introspection will determine the dependencies and inject them when constructing the object.
- Setter Based Injection (or Type 2): In this the dependency on collaborators is declared using setter methods (a la JavaBean style).
- Interface Based Injection (or Type 3): In this type the dependency is defined using interfaces.
Essential Tensions
Constructor Vs Setter
Constructor-based injection wires up the dependencies at object construction time. This gives a valid object at creation. However if there are many dependencies, the constructor arguments can become large and cumbersome. The setter based construction is simpler, more intuitive and provides flexibility that might not be available with constructors.
Configuration file vs Java Code wiring
Configuration file gives more flexibility and the advantages are obvious. The dependencies can be modified just by modifying the config files without requiring a compile or build. The Java code wiring is useful in situations where component assembly is complex and requires logic (if then else) – in those scenarios expressing dependencies using config files becomes difficult. Only logical choice is a programming language with rich sematics for complex logic. Java code wiring is going to be simpler and easier. A dynamic language like Groovy can be really useful here since being dynamic it won’t require compilation whereas Java would.
Advantages
- Removes code clutter, leading to more readable code.
- Provides a great deal of convenience - one can focus on implementing the business use case at hand rather than worrying about all the plumbing that sets up all the collaborators.
- Provides a great deal of flexibility - if one needs to have a different version of collaborator (i.e. different implementation), simply modify the dependency in configuration file and that's it. No code anywhere need to be modified. IoC greatly facilitates building loosely coupled systems.
- Simplifies unit testing - one of the greatest pain points with the heavyweight containers such as J2EE (EJB in particular) is that unit testing is very difficult. With IoC framework, one can very easily write up a mock object that implements the collaborator interfaces and configure this mock object. This flexibility is one of the key reasons for the current popularity of light-weight containers.
Popular DI/IoC Frameworks