In computer science, a dependency occurs when one component (class, module, etc.) relies on the functionality of another component to operate correctly. For example, a Laptop component depends on a Battery component to provide power.
Dependency Injection (DI) is a powerful technique that helps manage dependencies between components. Instead of a component directly creating the objects it depends on, those dependencies are provided (“injected”) from the outside.
This leads to several key benefits.
Consider the Laptop and Battery scenario. Here's a contrast between traditional instantiation and using Dependency Injection:
Improved Testability: DI makes unit testing a breeze. You can easily replace a real dependency with a mock or stub during testing, allowing you to isolate the component you’re testing and focus on its specific logic.
Loose Coupling: Your classes don’t have to be tightly bound to specific implementations of their dependencies. This means pieces of your code become more independent, leading to better maintainability.
Flexibility: You can easily change the underlying implementations of dependencies without breaking the classes that use them. For example, switching from a database logger to a file logger becomes a simple configuration change.
Reusability: Since components are not responsible for creating their dependencies, they become more reusable in different contexts.
Increased Complexity: Introducing DI, especially when done manually, can add a layer of complexity to your code with more classes and interfaces to manage.
Boilerplate Code: Some DI setups can involve writing a decent amount of code for object creation and dependency wiring, though this can be mitigated with DI frameworks.
Overuse: It’s possible to overuse DI, even in cases where it provides minimal benefit. This can lead to unnecessary complexity.
1. Constructor Injection
How it works:
2. Setter Injection
How it works:
3. Interface Injection
How it works:
1. Ambient Context
How it works:
AmbientContext.initialize(batteryInstance)
).
2. Service Locator
How it works:
ServiceLocator().register<Battery>(batteryInstance)
).
Also published here