The concept of Dependency Injection (DI) exists for a while. It was defined by Martin Fowler from the larger concept of Inversion of control (see here). This concept was defined as a mean to "wire" components together. Buy what does the term "wire" mean precisely? I think it's time to give to this concept of DI, that is apply in a vast area of frameworks, a more formal definition.
Definition
So what is Dependency Injection? Well, DI is just a form of generic programming. Usually genericity is obtained by applying parametric polymorphism to classes at the language level to get special parameterized classes or class templates. Then these class templates can be specialized to obtain regular monomorphic classes.
In case of DI, Genericity is obtained using subtyping polymorphism when designing classes. Specialization is obtained by creating prototypes from these classes. The next sections will explain and give examples to this concept of genericity associated to DI.
Genericity using parameterized classes
Generic programming is usually implemented in languages (such like Java and C++) with parametric polymorphism. A class can be parameterized with one or more formal type (or value) parameters.
The following simple example shows how classes can be made generic using parametric polymorphism.
Class templates
The following pseudo code shows how to design generic classes using parametric polymorphism.
// class templating using parameterized polymorphism
// template pseudo language
template <class BookingDAO, long MAX>
@Singleton
class BookingService {
BookingDAO bookingDAO = BookingDAO.new
long count
void book(bookingDetails) {
if(count++ < MAX) {
// do something with bookingDAO
}
}
}
template <class BookingService>
@Singleton
class BookingController
BookingService bookingService = BookingService.new
public void confirm() {
bookingService.book(bookingDetails)
}
}
@Singleton
class JPABookingDAO {
// use JPA entity manager...
}
The code simply defines a generic class BookingService wich is parameterized with a BookingDAO type and a long value MAX. The class is also tagged with the singleton scope. A BookingController class template is also defined on the same basis.
Template specialization
The following code shows how class templates can be specialized with effective types and values.
//class template instanciations
class BookingService<JPABookingDAO, 100> {
}
class BookingController<BookingService> {
}
Object lifecycle
One word about how object lifecyle is handled in the above example. To manage object lifecycles, we can use annotation based AOP. Classes can be tagged with the scope singleton. Then the annotation will be used by the AOP singleton aspect to intercept any call to the class constructor and return the same instance each time.Genericity using DI
Dependency injection is based on subtyping polymorphism and prototype wiring. The following model shows the same example as above but using DI instead.
Generic class design
Classes can be made generic using subtyping polymorphism. Classes must reference other classes through polymorphic references to interfaces (or abstract classes) in order to allow the substitution by subtypes later in the prototyping phase.
/**
*
* Spring version
* subtyping polymorphism + prototyping
*
*/
interface BookingDAO {}
class JPABookingDAO implements BookingDAO {
// JPA stuff ...
}
interface BookingService {
void book(Map bookingDetails);
}
class BookingServiceImpl implements BookingService {
long count;
BookingDAO bookingDAO;
long max;
public void book(Map bookingDetails) {
if(count++ < max) {
// do something with bookingDAO
}
}
}
class BookingController {
BookingService bookingService;
public void confirm() {
Map bookingDetails = new HashMap();
// set details
bookingService.book(bookingDetails);
}
}
Prototyping phase
The prototypes are created form the generic classes. During this phase, polymorphic references are resolved by the injection of a concrete subtype.
Te following code illustrates prototyping phase using the Grails BeanBuilder.
// prototyping
def bb = new BeanBuilder()
bb.beans {
bookingDAO(JPABookingDAO)
bookingService(BookingServiceImpl) {
bookingDAO = ref('bookingDAO')
max = 100
}
bookingController(BookingController) {
bookingService = ref('bookingService')
}
}
What does injection mean?
Injection (or component wiring) simply means the substitution of generic formal parameters by the effective parameters during the specialization.
Why using DI?
We have seen that DI is semantically equivalent to parameterized classes. Parameterized class-based genericity and load-time weaving AOP is a perfect combinaison to manage components. So why using DI? Simply because genericity based on parametric polymorphism is often poorly implemented in languages. In Java for example, class parameterization with primary values (String or int for ex.) doesn't exist. Other points are the use of proxy-based AOP with is more adapted to prototype-based programming and object lifecycle management.



