This is fully to provide the approach of loosely-coupled. the more objects depend on each other, the harder it gets to implement a new functionality. Service classes will be handling the changes and the end consumer will not be affected by the changes made. Like the end consumer will create/inject the service objects implementations of the provided. For these such cases if dependency providers are used e.g. spring, they will take care of creating, initialization, lifetime and the destruction of the objects.
Example situation and code
Let us imagine that we are building a system for a mobile telecommunication company, and we are asked to design an email transmitter application.
EmailService.java
public class EmailService { public void sendEmail(String message, String receiver) { System.out.println("Transmitted the message to " + receiver + " with the content: " + message); } }
MyApp.java
public class MyApp { private EmailService emailService = new EmailService(); public void processMessage(String msg, String rec) { this.emailService.sendEmail(msg, rec); } }
App.java
public class App { public static void main(String[] args) { MyApp app = new MyApp(); app.processMessage("Hello from java", "info@tugrulaslan.com"); } }
Output
Transmitted the message to info@tugrulaslan.com with the content: Hello from java
Here you observe that the overall design seems very simple. Now let us imagine that we have other request from the broadband company claims to have more service, for now they want SMS service to be integrated into our system.
We now ought to think that in the future there will be more systems and this proposal overall system is not agile to meet customer requests. At this point Dependency Injection will be emerging us towards the design issue. The below follow my lead and observe the design
MessageService.java
public interface MessageService { public void sendMessage(final String message, final String receiver); }
Here we are going to build an interface class which will preserve mutual methods which will be shared by concrete implementation classes. For now to expose the example we are going to simply type a singular method called “sendMessage”
EmailService.java
public class EmailService implements MessageService { public void sendMessage(String message, String receiver) { System.out.println("Transmitted the email to " + receiver + " with the content: " + message); } }
SmsService.java
public class SmsService implements MessageService { public void sendMessage(String message, String receiver) { System.out.println("Transmitted the sms to " + receiver + " with the content: " + message); } }
MyApp.java
public class MyApp { private MessageService messageService; public MyApp(MessageService messageService) { super(); this.messageService = messageService; } public void processMessages(String msg, String rec) { messageService.sendMessage(msg, rec); } }
In this class as you observe that there is a new method defined and called as “processMessage” the aim of definition will be explained, keep further reading.
App.java
public class App { public static void main(String[] args) { MyApp myApp = new MyApp(new EmailService()); MyApp myApp2 = new MyApp(new SmsService()); myApp.processMessages("Send an email with my warm regards", "info@tugrulaslan.com"); myApp2.processMessages("Sorry I am busy and cannot answer your call ", "+905003337788"); } }
Output
Transmitted the email to info@tugrulaslan.com with the content: Send an email with my warm regards Transmitted the sms to +905003337788 with the content: Sorry I am busy and cannot answer your call
To further explain and list out the above DI design;
- first of all we define mutual methods within an interface,
- accordingly create concrete classes of each of the services come up, which implements the shared interface.
- Later on we implement the methods in the concrete classes and fill them the way the request is being processed.
- We then create a main class which will allow us to allow us inject dependency via constructor, and then we define another method which calls the mutual method within the interface,
- Last of all, in the class where we will use the DI solution, We create an instanced of the class where the interface is being defined, and in the constructor we initiate the service we want to implement
- We appropriate we call the “proceedMessage”, which will be a trigger to call the mutual method.
In this aspect we simply have our own DI, which allows us to inject the service via constructor. Out there there are DI providers, which are world-wide-known. Below I am sharing a list of providers you can check up.
Those providers allow you to manage creation, initialization, behaviors, life time and destruction of the objects. You don’t have to deal with those activities.