Dependency Injection
Definition: Dependency Injection (DI) is a design pattern used in object-oriented programming where an object’s dependencies (i.e., the objects it relies on to function) are injected into it, rather than the object creating these dependencies itself. This promotes loose coupling, enhances testability, and makes it easier to manage and maintain the code.
Example in Python
Consider a scenario where you have a service that sends notifications. This service can send notifications via email or SMS.
- Without Dependency Injection:
1class EmailService:
2 def send_email(self, message):
3 print(f"Sending email with message: {message}")
4
5class SMSService:
6 def send_sms(self, message):
7 print(f"Sending SMS with message: {message}")
8
9class NotificationService:
10 def __init__(self):
11 self.email_service = EmailService()
12 self.sms_service = SMSService()
13
14 def notify(self, message, method):
15 if method == "email":
16 self.email_service.send_email(message)
17 elif method == "sms":
18 self.sms_service.send_sms(message)
19
20notification_service = NotificationService()
21notification_service.notify("Hello, World!", "email")
22notification_service.notify("Hello, World!", "sms")In this example, NotificationService directly creates instances of EmailService and SMSService. This makes the NotificationService class tightly coupled to EmailService and SMSService, making it harder to test and maintain.
- With Dependency Injection:
1class EmailService:
2 def send_email(self, message):
3 print(f"Sending email with message: {message}")
4
5class SMSService:
6 def send_sms(self, message):
7 print(f"Sending SMS with message: {message}")
8
9class NotificationService:
10 def __init__(self, email_service, sms_service):
11 self.email_service = email_service
12 self.sms_service = sms_service
13
14 def notify(self, message, method):
15 if method == "email":
16 self.email_service.send_email(message)
17 elif method == "sms":
18 self.sms_service.send_sms(message)
19
20email_service = EmailService()
21sms_service = SMSService()
22notification_service = NotificationService(email_service, sms_service)
23notification_service.notify("Hello, World!", "email")
24notification_service.notify("Hello, World!", "sms")In this version, the NotificationService class no longer creates instances of EmailService and SMSService itself. Instead, these dependencies are injected into it from the outside. This approach has several benefits:
- Loose Coupling:
NotificationServicedoes not depend on the concrete implementations ofEmailServiceandSMSService. It depends on their interfaces (in this case, the methods they provide). - Easier Testing: You can easily replace
EmailServiceandSMSServicewith mock objects when testingNotificationService. - Flexibility: You can change the implementations of
EmailServiceandSMSServicewithout modifyingNotificationService.
Summary
Dependency Injection helps in creating more modular, testable, and maintainable code by decoupling the dependencies from the class that uses them. This is achieved by injecting the dependencies from outside rather than letting the class create them internally.