Friday, March 1, 2013

Why Dependency Injection is a powerful concept


Dependency Injection (DI) is a type of  Inversion of Control (IoC) focused on decoupling and inverting the way an object obtains its collaborators.

In order to achieve its purpose, almost every object interacts with other objects, so there must be some code that materializes "the wiring" among them. 

The easiest way to do this would be to place that code in the object definition, that is to say, the object is responsible for obtaining its collaborators and one of the places of choice is its constructor.

Let's exemplify this with some Java code:

public class MyBusinessObject {

       private OtherBusinessObject obo;

       public MyBusinessObject(){

           obo = new OtherBusinessObject();

       }

      ...
}

Very simple, but it has some flaws. Let's say you programmed your objects thinking on
polymorphism and its underlying concept exchangeable objects, for leveraging code scalability. In Java, this can be achieved by defining the collaborator type as a superclass (probably abstract) or as an interface.

What if a new business requirement makes your object interact with a different class of its collaborator? 

Although small, some changes have to be made in the code (the collaborator instantiation in the constructor).

Furthermore, what if there are some scenarios in which the object has to interact with different definitions of the collaborator (needless to say all polymorphic among each others)? 

This is very common when unit testing: the object is tested isolated so you make it interact with a "fake" version of its collaborator (aka mock).

Clearly this approach is not flexible enough. So, how can we avoid modifying the code and support the ability of interacting with different objects at different scenarios? 

Here is where DI comes to action.

The concept behind


The core concept of DI is to move the responsibility of "the wiring" from the object to somewhere else, so the collaborators are provided to the object rather than the object trying to look for them. 

In this point is where the inversion of control takes place, as the control of connecting object is handled by some sort of underlying infrastructure. 

In the example above, instead of creating a new instance in the constructor, the object defines some mean (a parameterized constructor or a setter method) for assigning the collaborator from "outside".

Pretty simple. And if you think about it, it makes sense as it favors a clear responsibility division: the object it's only responsible for what is meant to be, the business logic.

The code that connects the objects, let's call it underlying infrastructure, may be anywhere as long as it is decoupled from the business objects. There are some implementations of DI like Spring and Google Guice that name this infrastructure differently: application context for the former and injector in the last case.

But why something so simple as where the wiring takes place can make the difference?

Simple but powerful 


Based on polymorphism, the object's collaborator can be exchangeable and this can be exploited using DI in many ways that leads to several advantages.

The first one, already mentioned, is for unit testing, a "must have" thing. While in a real scenario the object is provided with the version of the collaborator that actually holds the business logic, in a testing scenario, the object can be provided with a mock version so it can be tested in an isolated way with no code modifications.

In a real scenario, a collaborator implemented most likely by you, is provided to the object. As the underlying platform is responsible of doing this, it may provide an enhanced version of that collaborator where all its method are database transactional, so you don´t have to program that. And the object doesn't even notice its collaborator has been enhanced with transactional capabilities.

There are several features, which are not specific business rules but required, that can also be part of this enhancement, like security or log. Actually, this kind of "enhancement" has a name: AOP

There are some DI implementations that takes this idea even further and enhance the collaborator with actually business logic. Spring released a module called Spring Data, that can derive database code (like JPA code) based on the signature of a method in an interface. For instance, given the following method signature:   

List<Person> findByLastname(String lastname);


It creates an object with a method that searches a Person by it´s last name, using JPA.

Many other features can also be achieved, like executing some code before or after the wiring takes place, validate pre and post conditions, etc.

And all this built upon dependency injection.


Conclusions


Dependency Injection though a very simple concept, as inverting the way an object obtains its collaborators, leverages good practices like unit testing.

But also allows the underlying infrastructure (implementations) to build several complex concepts like AOP or deriving specific business logic.

The fact that something so simple helps to build so much is what makes DI a powerful concept.


No comments:

Post a Comment