Dependency injection (DI) is a technique used to help inject dependent objects of a class. It is also one of the techniques used to implement Inversion of Control (IoC).
Dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies.
Wikipedia
Dependency injection involves four roles:
– the service object to be used
– the client object that is depending on the service(s) it uses
– the interfaces that define how the client may use the services
– the injector, which is responsible for constructing the services and injecting them into the client
Wikipedia
Dependency injection also allows for the separation of the creation of a client’s dependencies from the client’s behavior, thus allowing program designs to be more loosely coupled.
The three common ways of injecting dependencies into a client class are:
- Constructor Injection – the dependencies are provided through a client’s class constructor.
- Method Injection – the dependencies are provided through a public method of the client class.
- Setter Injection – the dependencies are provided through a public property of the client class.
Example of Constructor Injection
Constructor injection allows client dependencies to be injected using the client’s constructor. Consider the below code base.
From the above code base it can be seen that the Logger
class is tightly coupled to the EmailValidator
class, since a concrete implementation of the Logger
class is used within the EmailValidator
class. Also, the EmailValidator
is responsible for creating an instance of the Logger
.
If the Logger
class requires dependencies in a future feature, then the EmailValidator
class will definitely break and will be required to provide these dependencies in order to create a new instance of the Logger
class. This change will introduce more unconcerned logic in the EmailValidator
.
To improve this design and to ensure that the EmailValidator
has only a single responsibility, dependency injection with constructor injection can be used to inject a Logger
dependency into the EmailValidator
class.
Using the roles of dependency injection, the following solution can be constructed:
interface | An interface can be created for a logger. This interface will be the contract that is used between the EmailValidator and the Logger class. This interface shall be called ILogger . |
service | The Logger class is known as the service class. This class will implement the ILogger interface. It is also known as a concrete implementation of the ILogger interface. |
client | The EmailValidator class is known as the client class. In this example, the EmailValidator has only one dependency, that is, an instance of the ILogger interface. An instance of the concrete implementation of the ILogger interface shall be injected into the EmailValidator class during run time. |
injector | There are many dependency injection frameworks (example Microsoft Dependency Injection, Autofac, StructureMap, etc.) that can be configured to associate the concrete Logger to the ILogger interface. These frameworks will handle the injector capabilities. |
Consider the below possible constructor injection solution.
The above solution has now decoupled the Logger
class from the EmailValidator
class. The EmailValidator
can focus on its intended behavior of validating email addresses and does not need to worry about constructing a Logger
instance.
Example of Method Injection
Method injection allows client dependencies to be injected into a client’s public method.
Method injection could also be used to solve the tight coupling between the EmailValidator
and Logger
class defined above.
Consider the below possible method injection solution.
Method injection is useful and is normally used when only a single method in a client’s class requires a dependency.
Example of Setter Injection
Setter injection, also known as property injection, allows client’s dependencies to be set through a client’s public property.
Setter injection could also be used to solve the tight coupling between the EmailValidator
and Logger
class defined earlier in this article.
Consider the below possible setter injection solution.
As can be seen from the above class, an instance of the ILogger
is injected using the public Logger
set property.
Summary
Dependency injection is a powerful design pattern that allows development of loosely coupled software components, thus enhancing re-usability, extensibility, test-ability, simplicity and maintainability.
There are a number of dependency injection packages that assist with creating an injector container. Choosing the appropriate dependency injection package normally comes down to personal preference.
Further information on dependency injection can be found at the following link: