Dependency and dependency injection
Tags: oop, java, springboot, programming
Published: 2023-06-26 10:25:08

Ever wondered how to effectively manage relationships between objects in object-oriented programming? Curious about a technique that can structure, describe, and optimize these connections? Well, have you heard of dependency injection? In this introduction, we'll delve into the world of dependency injection and explore its role in managing object dependencies. Ready to uncover the secrets behind this powerful concept? Let's dive in!

**Dependency and dependency injection**

Before we proceed, it's important to understand the term **"dependency."** In object-oriented programming, a dependency is a relationship between two classes where one class relies on the functionality of the other. Simply put, it means that one class is dependent on the other.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4otqrqsvksibo35d7qvd.png)




In OOP, dependencies appear in the form of instance creation inside a class. Through this instance, one class can access the methods of another class. This way classes become dependent.

Now, we will depict an example of a dependency in form of pseudocode. Here we create an instance of the `Email()` class inside of `EmailService()`:

```
class Email is
  method sendMessage() is
    ...

class EmailService is
  Email gmail = new Email()

  method sendMessage() is
    gmail.sendMessage()
```

**Dependency injection** or **DI** is the process of transferring the task of object creation to other parts of code. This means making a dependency that you can use. This allows the creation of loose coupling in our code.

**DI example**

In the first snippet, the `EmailService` class relies on the `Email` class. With this dependency, you cannot use different types of `Email`. Its instance is constructed inside `EmailService`, so you can't use different variations of it. Also, you can't test different `Email` instances.

To overcome this, let's try to use one type of DI on this code:

```
interface Email is
  method sendMessage()

class GmailService implements Email is
  method sendMessage() is ...
  ...

class EmailService is
  method sendMessage(Email service) is
    service.sendMessage()
```

Now, `EmailService` relies on the interface instead of a class. There are two classes that use our `Email` interface as a pattern and build different implementations. Object creation is now moved to other classes and the interface provides EmailService with different implementations. You can also set the implementation you want to access through the `sendMessage()` method:

```
EmailService gmail = new EmailService()
gmail.sendMessage(new GmailService())
```

But this is just the tip of the iceberg. There are different types of dependency injection that you need to take into consideration.

**Types of injection**

There are three main types of DI. Let's look at them.

- **Method(Interface) Injection** — dependencies are passed through methods that the class can access via interface or another class. The previous snippet of pseudocode was an example of method injection.
- **Property(Setter) Injection** — dependencies are passed through a separate setter method. Implementation of this injector varies depending on the language. Here is a bare-bones example of this injection in pseudocode:
    
    ```
    class EmailService is
      Email service
    
      method getService() is
        return service
    
      method setService(Email service) is
        this.service = service
    
    class Main is
      EmailService email = new EmailService()
      email.setService(new GmailService())
    
      Email gmail = email.getService()
      gmail.sendMessage()
    ```
    

Depending on the language, you can work with the `set` method in different ways. For example, in Java, using Spring annotations, you can include parameters from config files into the `set` method. However, this pseudocode is meant to be as simple as possible.

- **Constructor Injection** — dependencies are provided through the class constructor. Here we have our email example, where we can construct our `EmailService()` with different implementations of `Email()`:
    
    ```
    class EmailService is
      Email email
    
      constructor of EmailService(Email email) is
        this.email = email
    
      method sendMessage() is
        email.sendMessage()
    
    class Main is
       EmailService gmail = new EmailService(new GmailService())
       gmail.sendMessage()
    ```
    

**Why do we need DI?**

DI allows us to make code, parts of which are less interdependent on each other. Through DI, we can release some classes from the object creation process and pass them to injectors that can provide said objects. This way, one class will know nothing about the object creation process in the other. It allows you to reuse dependent classes without adjustment for each new instance of an object.

This comes in handy when you need to extend and modify your code. But, it can also improve your testing process. With DI, you can provide different instances with different tweaks of the same object, you can test how your code will react to it.

But, there are some disadvantages to DI. This can be addressed to the complexity of its implementation. It includes the steep learning curve for DI, problems caused by the overuse of DI, and troubles implementing DI with different frameworks and libraries.

**Conclusion**

Let's summarize this topic:

- The main point of DI is to loosen coupling. It helps you work with dependencies.
- By using DI you can increase code flexibility and simplify the testing process.
- It's a complex topic with different implementations based on the scenario.
- DI in different languages has peculiarities that can affect how you work with it.

While dependency injection can be a complex process, you now have a basic understanding of what it is and how it functions. If you're interested, there is plenty more to explore about dependency injection and its implementation. Feel free to delve deeper into the subject and expand your knowledge independently.