What is dependency injection (DI)?
A class often needs other objects to do its job. These are its dependencies. With dependency injection (DI), the class does not create them with new. Someone else creates them and hands them in. The usual way is constructor injection: the dependencies arrive as constructor parameters. This shows clearly what the class needs, and the class keeps them for as long as it lives.
Think of it as “give me what I need” instead of “I’ll secretly create what I need.” DI works well with interfaces, but you can also inject a class that has no interface.
Why use it?
- Testability: In a test, you can pass a fake (mock) object, so no real email gets sent.
- Flexibility: You can switch implementations (e.g. Email vs SMS) without changing the class that uses them.
- SOLID (DIP, the Dependency Inversion Principle): High-level code (the business logic) depends on abstractions (interfaces), not on low-level details such as a specific email sender. The caller decides which implementation to inject.
Example: constructor injection
// High-level service depends on an abstraction (interface)
public class NotificationService {
private final NotificationChannel notificationChannel;
// Dependency is INJECTED via constructor — we don't do 'new EmailService()' here
public NotificationService(NotificationChannel channel) {
this.notificationChannel = channel;
}
public void notify(String msg) {
notificationChannel.send(msg);
}
}// Caller (e.g. Main) decides which implementation to use
NotificationChannel email = new EmailService();
NotificationService service = new NotificationService(email);
service.notify("Hello");
// Or swap to SMS without changing NotificationService
NotificationService smsService = new NotificationService(new SMSService());
smsService.notify("Hello");NotificationServiceonly knows theNotificationChannelinterface. It has no idea whetherEmailServiceorSMSServiceis behind it. This is the inversion: the service depends on the interface, and the caller injects the real channel.
Where you see it in this repo
- DIP GoodCode:
NotificationService(NotificationChannel channel)— constructor injection.MaincreatesEmailServiceorSMSServiceand passes it intoNotificationService. This is the main example of DI in the SOLID section.examples/ConceptsDemo.javashows the same idea:AlertServicereceives aNotifierin its constructor.