Pages

Wednesday, 8 July 2015

Dependency Injection. What is it?


General:

Dependency injection (DI) is a prime technique for building loosely coupled applications. It provides ways to handle the dependencies between objects. For example, an object that processes customer information may depend on other objects that access the data store, validate the information, and check that the user is authorized to perform updates. Dependency injection techniques can ensure that the customer class correctly instantiates and populates all of these objects, especially where the dependencies may be abstract.

Dependency Injection is one of the priciples, namely the "Dependency Inversion Principle" of the SOLID (Definition)principles of Object Oriented Programming (OOP)

In object-oriented programming, the dependency inversion principle refers to a specific form of decoupling where conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are inverted (i.e. reversed) for the purpose of rendering high-level modules independent of the low-level module implementation details. The principle states:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
The principle inverts the way some people may think about object-oriented design, dictating that both high- and low-level objects must depend on the same abstraction.[1]

After all these standard definitions of the DI, we shall try to understand where the need of Dependency Injection came from by looking at the history of OOP and the steps of concepts which lead to DI.

The DI emerged from the need of loose coupling, or in other words to make objects (classes) interact loosely with each other. Then the second question may arise. What is loose coupling and why do we need it?
When we write a complex code, we usually need many objects (in form of classes) and the complex code shall need all those classes to interact with each other in one way or another, in order to accomplish a certain functionality of the system (i.e. for a banking system, customer classes interacting with specific account classes using calculation classes throughout the process).
In principle we can create just one huge class with one function (or method) and stuck everything into it in order to solve our problem. Is this viable? This was actually the functional programming in the 80s and 90s, where companies created their famous legacy codes. However, these companies realized after a while that they are hitting a roadblock when they wanted to expand the code, or maintain for bugs or perform testing on their code to validate it. The need for modular design started emerging along with the Object Oriented Programming and it's principles (refer to SOLID: Definition) .
Soon after some clever people in the software industry started coming up with certain design patterns, architectures and the concept of factoring (refer to Martin Fowler). Some of those design patterns provided some separation in between the interacting classes but were not perfect. Factory pattern, Abstract Factory Pattern, Service Locator pattern were a few of them but really did not satisfy the need for a good loose coupled architecture. (refer
Factories, Service Locators, and Dependency Injection <https://msdn.microsoft.com/en-us/library/dn178469(v=pandp.30).aspx> )

Those design patterns somewhat used the basic principle of Loose Coupling, which is shown below.

 

Basically, Loose Coupling is using an Interface (an intermediary plug) in between the high level class (HLC) and low level class (LLC). An HLC is a class where a detailed operation is performed (as an example some banking computation where interest rate is calculated) and LLC is the class which calls upon the HLC. Those two classes communicate with each other via an Interface. An Interface is a very important concept in OOP. An Interface is basically an empty shell of methods, properties etc. Only a class can implement an Interface method or property. Interface makes (for instance) polymorphism possible in OOP (for more about Interfaces refer to Interfaces (C# Programming Guide)).

1-Now let's take a simple example, using a low level class which inherits from an Interface:

LLClass:XYZ // LLClass inherits from XYZ INterface
{
}

2-Now for the High Level Class:

HLClass
{
Interface XYZ; //Declare Interface
LLClass LLCls = new LLClass(); //Create LLClass
XYZ = LLClass; // Assign the Interface to the Low Level Class, as we want to use the Interface (plug to the LLClass) within the High Level Class
}

Now we can use the Interface (the plug to our Low Level Class) from within our High Level Class. But wait, there is already a problem. We have created an instance of the Low Level Class within the High Level Class, making the HLC dependent to the LLC. So, even though we have an interface at the HLC to plug in to LLC thus avoiding the use of LLC directly, we kept the dependency (to the LLC) when we created the LLC in the first place. How can we avoid this? Well, this is where the Dependency Injection (DI) comes into play.

Idea: We shall create the LLC somewhere else (for instance another class we call a "creator" class) and then inject the Interface XYZ somehow into our HLC. Now, let's look at the following case.

LLClass:XYZ // LLClass inherits from XYZ INterface
{
}

2-Creator class

clsCreator
{
clsCreator()
{
LLClass LLcls = new LLClass(); //Create LLClass
HHClass HHCls= new HHClass(LLCls); //Create HLClass
}
}

3-Now for the High Level Class:

HLClass
{
Interface XYZ; //Declare Interface

HLClass(Interface xyz) // Constructor passing Interface XYZ
{
XYZ = xyz; // Assign the Interface to the Low Level Class, as we want to use the Interface (plug to the LLClass) within the High Level Class
}
}

As we can see now the HLC and LLC are completely separated (i.e. independent of each other), in other words they are loosely coupled via the concept of Dependency Injection. We have simply injected the XYZ Interface (which is a plug to the LLC) into the HLC via the constructor. A similar injection could have been made by using a method or property.

Unity Dependency Container is simply a lightweight framework doing just this plus other goodies. We can use (i.e. add) Unity into our projects (refer to
Adding Unity to Your Application) and start using it for a modular, loosely coupled, easily expandable, maintainable and testable software system design.

No comments:

Post a Comment