Dependency Inversion: Practical Example

Dave Salazar
3 min readMay 3, 2021

Introduction

If you have studied the SOLID principles, you came across with the last but most important principe, Dependency Inversion. This is a way to decouple dependencies using interfaces, so our system can be maintainable and flexible. As uncle bob say:

The Dependency Inversion Principle (DIP) tells us that the most flexible systems are those in which source code dependencies refer only to abstractions, not to concretions.

I cannot express how important this principle is to the life of a developer, the magic that can be achieved is priceless. But at first it could be really confusing.

What does it mean? how is implemented? and how could be useful?

If I have to describe it the most clear and concise as possible:

Any external code (libraries, frameworks or other dependencies) must be tied to an interface that will be from which our code will access these resources.

In this article I going to show you an example of how can be used in Java. So, let’s dive into it

Send an email for a registered user

Let’s try with a email service example. We have to send an email each time an user is registered

first you will have a Email class :

class Email {
private String from;
private String to;
private String body;
public Email(String from, String to, String body) {
this.from = from;
this.to = to;
this.body = body;
}
public String getFrom() {
return from;
}
public String getTo() {
return to;
}
public String getBody() {
return body;
}
}

Then a UserRegister class with a method to send the email:

class UserRegister {    public void sendEmail() {
Email email = new Email("from@test.com", "to@test.com", "Thanks for register to our page");
/*
implement the email service dependencies
*/
System.out.println("The mail has been sent!");
}
}

and then you could just send it:

public class Application {  public static void main(String[] args) {        UserRegister register = new UserRegister();        register.sendEmail();
}
}

Perfect! now you can send emails right?

Now, the company subscribe to an email service that has different dependencies, but the system is already coupled to the first email service.

What you can do is create another method with this specific email service, and this could happen many times. So sooner or later you could ended up with something like this:

class UserRegister {    public void sendEmail() {
Email email = new Email("from@test.com", "to@test.com", "Thanks for register to our page");
/*
implement the email service dependencies
*/
System.out.println("The mail has been sent!");
}
public void sendEmailWithOtherService1() {
Email email = new Email("from@test.com", "to@test.com", "Thanks for register to our page");
/*
implement the other email service1 dependencies
*/
System.out.println("The mail has been sent!");
}
public void sendEmailWithOtherService2() {
Email email = new Email("from@test.com", "to@test.com", "Thanks for register to our page");
/*
implement the other email service2 dependencies
*/
System.out.println("The mail has been sent!");
}
public void sendEmailWithOtherService3() {
Email email = new Email("from@test.com", "to@test.com", "Thanks for register to our page");
/*
implement the other email service3 dependencies
*/
System.out.println("The mail has been sent!");
}
}

And so things can get out of control

Dependency Inversion to the rescue!

You can notice that all of those services receive the same parameter, and ended up making the same action. With that in mind let’s create an interface EmailSender:

interface EmailSender {
void sendEmail(Email email);
}

Pretty simple right?

Now, for each of the email services create a class that implement the EmailSender interface:

class EmailService1Sender implements EmailSender {    @Override
public void sendEmail(Email email) {
System.out.println("The mail with email service 1 has been sent!");
}
}
class EmailService2Sender implements EmailSender { @Override
public void sendEmail(Email email) {
System.out.println("The mail with email service 2 has been sent!");
}
}
class EmailService3Sender implements EmailSender { @Override
public void sendEmail(Email email) {
System.out.println("The mail with email service 3 has been sent!");
}
}

now your UserRegister class look like this:

class UserRegister {    private final EmailSender emailSender;    UserRegister(EmailSender emailSender) {
this.emailSender = emailSender;
}
public void sendEmail() {
Email email = new Email("from@test.com", "to@test.com", "Thanks for register to our page");
emailSender.sendEmail(email);
}
}

This is so much clean!

what we have just accomplished is very important. Our UserRegister does not know what service he is using, he only knows that it sends emails and that is all he should know

You can download the code here:

DaveSalazar/java-dependency-inversion-example

--

--