What is “Dependency Injection”?

What are dependencies?

In programming, a “dependency” represents the relationship between different components in a software system. When one component uses or depends on another component, it is considered that there is a dependency between them.

Addiction can take many forms, and the basic types of addiction include:

  • Class Dependency refers to a situation where one class uses another class. For example, if you have a class that represents the user (User) and another class that manages the database (DatabaseManager), the user class can depend on the database management class to store or retrieve information.
  • Module Dependency occurs in larger systems, when different components are often organized into modules. Dependencies between modules can occur when one module uses the functionality or services of another module.
  • Method Dependency occurs when one method calls or uses another method.

  • Service Dependency occurs when an application uses different services, such as data management services, authentication services, or messaging services. Service dependency occurs when a piece of code (eg a class) uses the functionality or services of a service.

Dependencies are not inherently bad, but it is important to manage them in a way that makes the code easier to maintain and test. But excessive dependency and “strong coupling” (eng. Strongly Coupled Code) between components can make code changes and code unit testing difficult.

Component Connections

“Strongly Coupled Code” (eng. Strongly Coupled Code)

In programming, “Strongly Coupled Code” refers to a situation where system components have strong dependencies on each other, i.e. one component uses another component or when one module uses another module, then changes in one part of the code often require changes in other parts that depend on them.

The downside of this design is that code written this way can be more difficult to maintain because changes in one part of the code can have cascading effects on other parts. Flexibility is also reduced because replacing or upgrading parts may require wider changes to the entire system.

An example of “Strongly Coupled Code” is “directly instantiating objects within one component” that depend on implementations of other components or directly using concrete classes instead of abstractions or interfaces for communication between components.

“Loosely Coupled Code”

Loosely Coupled Code refers to a design where system components have minimal dependencies on each other, so changes in one part of the code should not cause significant changes in other parts.

In this design, components often communicate via interfaces or abstractions instead of directly depending on implementations of other components.

The advantage of this design is noticed in maintenance because changes in one part of the code will not automatically require changes in other parts, thus increasing the flexibility of the system because it is possible to replace or upgrade parts more easily without affecting the entire system.

What is “Dependeciesinjection”?

“Dependencies injection” is a software design pattern based on the rule that we pass objects to other components that need them (“inject”), instead of the components themselves creating them inside.
Such an approach contributes to the reduction of mutual dependence between components (“Loosely Coupled Code”). Dependencies (“dependencies”) whose instances are passed must have interfaces or be abstract themselves. This approach to programming also facilitates unit testing because mock implementations of dependencies can be easily inserted during testing.

There are three main ways to inject dependencies from the outside:

  1. Constructor Injection: dependencies are injected through the class constructor

  2. Method Injection: dependencies are injected via methods

  3. Property Injection: Dependencies are injected via properties

Examples

In this example, we will consider two interconnected classes, the “Car” class and the “Motor” class. It is clear to everyone that the “Car” class depends on the “Motor” class, ie. that “Motor” is a dependency of the “Car” class. In the examples, the “Motor” class has only one method called “Start”, and this is exactly the method that the “Car” class needs.

car injection

Example of Strongly Coupled Code

The “Engine” class in this case looks like this:

With the “Strongly Coupled Code” design, the dependent object is directly instantiated, so in this example we will directly instantiate the “Motor” object as part of the constructor:

So in the main program it would look like this:

This code looks quite regular and OK until there is a need to expand the capabilities of the application, so a new engine needs to be inserted, e.g. electric. Then the problem arises that in addition to the new class for the engine, we also have to make changes in the Car class, that is. most likely to make two separate classes of cars each with its own engine!

Example of loosely coupled “Loosely Coupled Code” with “dependency injection”

In this example we need to add an interface for the Start:

method

So we will change the “Engine” class to implement this interface:

With dependencies injection we can easily add new engines, it is enough to just create a new class for the new engine, e.g. “MotorElectric” which implements the same IMotor interface:

A class that implements that interface:

So the Car class will now have the following form:

In the main program, you can now see that the created instance of “Motor” is passed to the “Car” class as a parameter:

CONCLUSION:

In the example with Dependency Injection (“Loosely Coupled Code”), the Car class uses the IMotor interface, and the concrete implementation (Motor) is injected via the constructor. This allows easy replacement of the engine implementation.
While in the example without Dependency Injection (“Strongly Coupled Code”), the Car class directly instantiates the Engine, which makes the code less flexible, and if we want to change the engine type, we would have to directly change the Car class.

Using Dependency Injection provides greater flexibility and makes code easier to test and maintain, making it less dependent on specific implementations.