28 Oct 2023
Dependency Injection (DI) is a software design pattern that allows for loose coupling between classes and their dependencies. It does this by decoupling the usage of an object from its creation. This makes code more reusable, testable, and maintainable.
In Dependency Injection (DI) , you don't create the objects that your code needs. Instead, you inject them into your code. This is done through the constructor of the object, or through a setter method.
Simple definition of Dependency Injection (DI) in points:
- Dependency: A resource that a given piece of code requires.
- Injection: The process of supplying a dependency to a piece of code, rather than being created internally.
- Dependency Injection: The process of supplying a dependency to a piece of code without having to create it ourselves.
DI is useful for a number of reasons, including:
- Loose coupling: DI helps to make code more loosely coupled, which means that classes are less dependent on each other. This makes code more reusable and easier to test.
- Testability: DI makes code more testable because we can easily mock or stub out dependencies for testing purposes.
- Maintainability: DI makes code more maintainable because it makes it easier to change the implementation of dependencies without having to change the code that uses them.
Simple example of DI
Let's say we have a class called Car that depends on a class called Engine. Without DI, the Car class would create its own Engine object. This would make the Car class less reusable, because it would only be able to work with a specific type of Engine.
With DI, we can pass the Engine object to the Car class when it is created. This makes the Car class more reusable, because it can now work with any type of Engine object that is passed to it.
// Define the Engine class
class Engine {
// Method to start the engine
void start() {
System.out.println("Engine starting...");
}
}
// Define the Car class
class Car {
private Engine engine; // Declare an instance variable to hold the Engine object
// Constructor that takes an Engine object as a parameter
Car(Engine engine) {
this.engine = engine; // Assign the passed Engine object to the instance variable
}
// Method to drive the car
void drive() {
engine.start(); // Call the start method of the Engine object
System.out.println("Car driving...");
}
}
public class Main {
public static void main(String[] args) {
// Create an Engine object
Engine engine = new Engine();
// Create a Car object and pass the Engine object to it
Car car = new Car(engine);
// Drive the car
car.drive();
}
}
Diagram:
+---------------+
| Engine |
|---------------|
| start() |
+-------|-------+
|
|
|
v
+---------------+
| Car |
|---------------|
| engine: Engine|
|---------------|
| + Car(engine) |
| + drive() |
+---------------+
Explanation of the example code in points:
-
Engine Class:
- We define a class called
Enginethat represents an engine. - It has a method
start()to simulate starting the engine, and it prints a message.
- We define a class called
-
Car Class:
- We define a class called
Car, which represents a car. - The
Carclass has a constructor that takes anEngineobject as a parameter.
- We define a class called
-
Dependency Injection (DI):
- DI is demonstrated by passing an
Engineobject to theCarclass when it is created. This allows theCarclass to work with any type ofEngineobject, making it more reusable and flexible.
- DI is demonstrated by passing an
-
Constructor Injection:
- In the
Carconstructor, we use constructor injection to assign the providedEngineobject to an instance variable, making it available for use throughout theCarclass.
- In the
-
Drive Method:
- The
Carclass has adrive()method that simulates driving the car. - Inside the
drive()method, it calls thestart()method on theEngineobject to start the engine and prints a message indicating that the car is driving.
- The
-
Main Function:
- In the
mainfunction, we create an instance of theEngineclass, representing the car's engine.
- In the
-
Creating a Car Object:
- We create a
Carobject and pass theEngineobject as a parameter to theCarconstructor, demonstrating how DI allows us to provide dependencies to objects from outside.
- We create a
-
Driving the Car:
- Finally, we call the
drive()method on theCarobject, which starts the engine and prints a message indicating that the car is driving.
- Finally, we call the
-
Benefits of Dependency Injection:
- This example illustrates the benefits of Dependency Injection in making code more reusable and testable.
- It allows for the separation of concerns, where the
Carclass doesn't need to create its ownEngineobject but relies on an external entity to provide it.
-
Testing:
- DI makes it easier to test components in isolation, as you can provide mock or test-specific implementations of dependencies when testing the
Carclass.
- Flexibility:
- With DI, you can easily switch the type of engine or other dependencies used by the
Carclass without modifying theCarclass itself, promoting flexibility and maintainability in your code.