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.