This is an acronym for:
S — Single Responsiblity principle
O — Open Closed Principle
L — Liskov Substitution Principle
I — Interface Seggregation Principle
D — Depencency Inversion Principle
Single Responsiblity Principle
There should never be more than one reason for a class to change.
In simple words it means a class should be focused or handle single responsibility and it must address just a specific concern.
Let’s understand this with the help of an example:
Let us say we have a class say ExampleClass this is interacting with server X. Over time there had been some changes in the way the message could be sent. The changes could be
- Change in protocol (Http → Https)
- Change in message format (JSON → XML)
- Change in security protocol ( Added Authentication)
So, if these chnages has to be implemented then insted of getting this implemented in just one method as a large code whenever a message format chnage or a protocol change. Each reponsblity should be handled by a different module or class insted being handled in a same class. To extract the complexity of future code change whenever required.
Open Closed Principle:
It states that a class, module or derivatives should always be open for extension but closed for modifcation. i.e Let’s say there is a base class with method X and class Y wants to use X with some changes then insted of using the method of X directly by modifying the method of X, it should extend X and override the required method.
If you are familiar of any OOPs language then you must be aware of that.
Liskov Substitution principle
It states that a we should be able to substitute a base class object with a child class object and this should not alter the behavior/characterstics of program.
Interface seggregation Principle
Client should not be forced to depend on the interfaces that they do not use. This is also called Interface pollution meaning w should not have a very large list fo unneccesary methods in a single interface.
- * Write highly cohesive method.
Dependency Inversion
High level modules should not depend on low level modules. Both should depend upon abstraction.
Abstraction should not depend upon details rather details should depend upon abstractions.
In simple terms,
Dependency Inversion is the strategy of depending upon interfaces or abstract functions and classes rather than upon concrete functions and classes.
Let’s try to understand this with the help of an example. First we will wirte a class not adhering the dependcy inversion, then will see the challenges it was facing and then will come up with the dependency inversion solution.
Let’s say we have a EmailService class which is responsible for sending notifcation to the user.
class EmailSerivce{
public void sendMessage()
{
System.out.println("send message");
}
}
class NotificationService{
public void sendNotification(String message)
{
EmailSerivce emailService = new EmailService();
emailService.sendMessage();
}
}
Here we have implemented the notification service to send the email. Now, let us try to understand the tight coupling that we have done and what would be it’s after impact.
Let’s us now consider a secnario where the business came up with an idea of sending the notification through both message and email (or message or email). The problem arises here. Since the whole logic is hard coded now we need to re write all of the logic for both the service if we are proceeding with the same approach.
Now let’s see how dependency inversion comes to rescue. So dependency inversion in simple terms says insted of depending on concrete classes or functions we should depend on abstract classes or modules and we can pass these dependency to the dependent class rather than hardcoding in the code.
Let’s see the refactored code and advantages.
interface NotificationProvider{
void sendMessage(String message);
}
class EmailNotificationProider implements NotificationProvider
{
void sendMessage(String message){
System.out.println("Message sent from email");
}
}
class NotificationService{
void sendNotification(NotificationProvider np,String message)
{
np.sendMessage(message);
}
}
public static void main()
{
NotificationProvider np = new EmailNotificationProider();
NotificationService notificationService = new NotificationService();
notificationService.sendNotification(np,"Hello world");
}
Now let’s say some service B wants to send the notification via message then insted of changing the whole code logic a small change would be done as shown and everything will be working.
class SmsNotificationProider implements NotificationProvider
{
void sendMessage(String message){
System.out.println("Message sent from email");
}
}
public static void main()
{
NotificationProvider np = new SmsNotificationProider();
NotificationService notificationService = new NotificationService();
notificationService.sendNotification(np,"Hello world");
}
With just 1 line of chnage we are ready with the Sms service.