What is Dependency Injection Design Pattern?
The software world is getting more robust and smart with blooming innovative ideas every day. Every technology is lightened by this smartness which reduces the heavy and complex mechanisms. Emerging frameworks are best examples of those innovations. Those are capable of making the designers and developers’ lives less stressful with many automated features. Containers are a core part of these frameworks which assemble and manage components from different layers and external entities. Containers use several design principles to enhance their functionalities. Dependency Injection is a key principle in this context which simplifies complexities in systems.
How Dependency Matters?
Dependency is the need of data and services of one entity to continue the execution of other entity. The dependent entity will be unusable or worthless without the existence of service entity. In the programming world, if one object requires data and service of another object for its’ functioning we call it as a dependency and the service receiver is called as the dependent.
What is Dependency Injection Design Pattern?
The dependency injection design pattern is a way of object configuration to obtain independency of each object responsibilities. In there, a dependent object is configured from the outside instead of configuring inside the same object. It enables loose coupling of applications with a set of general guidelines and it is not a library, framework or tool. This also popular as Hollywood principle – “Don’t call us – we’ll call you”.
Instead of hard-coding dependencies, such as specifying a database server, list of services are injected into the component via a third party. So that component needs not to worry about creating and handling those external services. It leads to many benefits especially when the system gets larger and complex. Most of the time containers are used to manage and automate the construction and lifetimes of interdependent objects.
Origin of Dependency Injection
This is the code implementation style of famous ‘Dependency Inversion Principle’ which is the last design principle from the 5 SOLID principles of object oriented design by Robert C. Martin. This principle ensures that any dependency of an object should be provided externally instead of dependency internally created within the same object. The two main points of dependency inversion principle are,
- High-level modules should not depend on low-level modules that is a module should depend on abstractions, not concrete details.
- Abstractions should not depend upon details. Details should depend upon abstractions.
Hence, Dependency Injection design pattern invents approaches to fulfil above conditions of the dependency inversion principle.
What is Injection?
Injecting is inserting or passing the data or service to the dependent entity. A dependency can be pushed into the class from the outside.
How does Dependency Injection work?
Dependencies can be injected into the object via several methods. All those methods should ensure external object instantiation without disturbing the client code. The client or dependent object delegates the responsibility of providing its dependencies to external code. This external code could be called as ‘Injector’. Injector plays the mediator role to introduce client and its dependency to each other. The client need not call or deal with injector code. It is done by separate service. Primary approaches for injecting the dependencies are via constructors and setter methods. But now there are specialized dependency injection frameworks as well to acquire this requirement. Usually, a container manages the lifecycle of objects and their dependencies based on a configuration file or annotations.
Simple Real Life Example
Can you live without using a toothbrush? It is a – can’t live without, item on your personal care item list. But, have you ever thought how and where your toothbrush was made? Most probably, ‘No’ or ‘Who cares?’ will be your answers. Of course, it is totally out of our scope. Making a standard toothbrush is a complex, time-consuming process. It needs a list of production materials and labor aligned with a standard process before coming to our hand. So, no way we can build it at our home. The toothbrush manufacturer does it for us. We just have to buy it from the shop and use it. In this scenario, you depend on the toothbrush to clean your teeth. Since, you can’t make the toothbrush by your own, the manufacturer makes that for you that is manufacturer inject the toothbrush dependency on yourself so that you can use it to keep you clean. Likewise, dependency injection is a way of providing an external data or service without interrupting individual existence of an entity.
Without Dependency Injection
Let’s assume that small business wants to keep track of their employees. It creates the ‘Employee’ class and stores the relevant data like name, id, address and phone number. Since the address contains several parts and it may vary depending on certain conditions designers decide to make it as a separate class. Now, when you are creating or using an employee object, an address object should be instantiated within the employee object in order to complete the proper behavior of employee object.
Let’s look at the code samples,
Problem
In any case, if the ‘Address’ class was modified as to insert another part to the address employee class should reflect necessary changes accordingly and re-compile again to function properly. Also, if address object gets to depend on any other external class that also will have to consider when managing the employee class. So, this makes the maintenance of the system very difficult.
Solution
Find a way to decouple the Address object from the Employee object. Separating each class responsibilities independently, by introducing a way to inject the service from Address object to Employee object. So that Employee object can behave independently.
Methods of Injecting Dependency
- Constructor Injection – Dependencies are inserted via class constructor
- Setter Injection – Client or dependency provider provides a setter method which is used by the injector()
- Interface Injection – The dependency provider introduces an injector method that will inject the dependency into the client
Let’s get to know the above method using the Employee/Address example. In all the below examples ‘Employee’ class is de-coupled from ‘Address’ class and ‘Address’ class also exists independently without interacting tightly with ‘Employee’ class. The address attribute or the dependency has to be injected to the dependent component via dependency injection mechanism.
Traditional approach to insert address to the employee object,
‘Employee’ class is responsible to initialize the address object and use it to send the greeting card. This is a hard-coded dependency, if there are any internal changes to ‘Address’ class, it will require code changes and recompilation of ‘Employee’ class as well. If the ‘Address’ object is used in any other classes in the same manner, all those classes have to be changed. These will make the application difficult to extend, maintain and test.