Item - 5 Prefer Dependency Injection to Hardwiring Resources
This writing will discuss item 5 in Effective JAVA, recommended by Joshua Bloch.
Dependency Injection is directly in contact with the Dependency Inversion principle in SOLID, a technique where the dependencies are provided to the class externally. This promotes coding with interfaces that make a system more modular, readable, reusable, and testable.
NOTE: Do not confuse dependency inversion with dependency injection. Dependency inversion is a philosophy and principle, but dependency injection is a technique providing this philosophy.
NOTE: Tightly couples the class with the particular resource, making it less flexible and harder to test. If you need to change the help, you must modify the class. This means Hardwiring Resources.
Dependency Injection and abstractions are significant for clarity, readability, maintainability, and clarity.
I want to explain why Dependency Injection is so essential. Robert C Martin mentioned bankrupt companies because of destructive code and mess. (You can read this story in Clean Code, Chapter 1 BAD CODE section) [2]
Of course, you have been impeded by bad code. [2]
If those companies had protected and implemented the modularity of the SOLID principles in the best way, they probably would not have gone bankrupt. But would the implementation of the complete dependency inversion principle, along with the dependency injection technique, have saved them? Probably not. That's why the SOLID principles and dependency injection are essential to implement best practices and develop new features.
Let's Look at the wrong example.
NotificationUtils.java
public final class NotificationUtil {
private NotificationUtil() {
}
public static void sendSms(String sms) {
// Some custom logic
System.out.println("sms sent");
}
public static void sendEmail() {
// Some custom logic
System.out.println("phone email");
}
public static void sendNotificationToPhone() {
// Some custom logic
System.out.println("phone notification");
}
}
Indeed, this usage is only partially suitable for the SOLID principle. This usage does not provide modularity. Not single responsible because doing many jobs. This usage is open the modification (Open Closed Violation). There is no interface segregation, and there is no dependency inversion. The notification system depends on this class, and this usage could be more flexible and maintainable.
Let's look at a good example.
Notification.java
public interface Notification {
void send(String title, String body);
}
Email.java
public class Email implements Notification {
@Override
public void send(String title, String body) {
System.out.println("Email sent --> " + title + "\n" + body);
}
}
Sms.java
public class Sms implements Notification {
@Override
public void send(String title, String body) {
System.out.println("SMS sent --> " + title + "\n" + body);
}
}
FMC.java
public class FCM implements Notification {
@Override
public void send(String title, String body) {
System.out.println("FCM phone notification sent --> " + title + "\n" + body);
}
}
NotificationManager.java
public class NotificationManager {
private final Notification notification;
public NotificationManager(Notification notification) {
this.notification = notification;
}
public void sendNotification(String title, String body) {
notification.send(title, body);
}
}
Test.java
public class Test {
public static void main(String[] args) {
NotificationManager notificationManager = new NotificationManager(new Email());
notificationManager.sendNotification("Verify Account", "body");
notificationManager = new NotificationManager(new FCM());
notificationManager.sendNotification("Verify Account", "body");
notificationManager = new NotificationManager(new Sms());
notificationManager.sendNotification("Verify Account", "body");
}
}
Output
Email sent --> Verify Account
body
FCM phone notification sent --> Verify Account
body
SMS sent --> Verify Account
body
Conclusion
Consistently enforce dependency injection for better performance, modularity, clarity, readability, and maintainability. Frameworks provide DI (Dependency Injection). Spring Boot includes multiple annotations for DI, such as @Autowired, @Service, @Bean, @Component, @Inject, and more. [4] Also, Spring provides DI with XML configs [5]
REFERENCES
Bloch, Joshua. Effective Java Programming Third Edition. [1]
Robert C. Martin, Clean Code. [2]
https://www.baeldung.com/java-dependency-inversion-principle [3]
https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring [4]