SOLID Principles Every Programmer Should Know

Peter Lee
An Idea (by Ingenious Piece)
8 min readJun 20, 2020

SOLID Principles involve 5 principles: Single Responsibility Principle(SRP), Open/Closed Principle(OCP), Livkos Substitution Principle(LSP), Interface Segregation Principle(ISP), Dependency Inversion Principle(DIP). It is the most important in Software Design Principles (based on Object-Oriented Programming).

SOLID Principles. Source: Internet

Before reading the article, you should read my previous article.

On the internet, you could search may topics/articles talking about this. Before writing this article I have searched on the internet but I couldn’t search for enough information that satisfies me. So, I have decided to write an article about Software Design Principles here. Not like the previous article which contains many theories in this article I will give you many case studies from simple to complex based on my experience. So, this article will contain a lot of codes that are written by Java language.
I very love a quote from Linus Torvalds: “Talk is cheap. Show me the code.”.
Let’s start !!!

Single Responsibility Principle (SRP)

“A class should have one and only one reason to change, meaning that a class should have only one job.”

Single Responsibility Principle (SRP)

Case study 01 (Simple)

I have worked on a lot of projects in many years. In my experience, The Helper/Utility class often violates this principle. A Helper/Utility class could contain from 1000 to 2000 lines. Please take a look at the below code:

MMCommonUtils class has many responsibilities.

Can you spot the problems? MMCommonUtils.java has many responsibilities. It violates with Single Responsibility Principle(SPR). Therefore, we should create three classes: MMImageMetadataUtils.java, MMQRCodeUtils.java, and MMCsvUtils.java to guarantee code abide by SRP.

Case study 02 (Use Decorator Pattern)

Problem: Assume you need to build a feature sending notification via email or slack. You can implement it like below class diagram:

Send notification via email or slack.

That looks good !!!. No problem with the above code but now we have to support sending notification via email and slack or email and SMS. You could implement like below diagram:

Send notification via email and slack or email and SMS.
We have to create a lot of classes for the above requirements.

If we implement like the above code when we want to support sending notification via Email and SMS or SMS and Slack or Email and Slack and SMS. We have to create a lot of classes(EmailAndSmsNotification.java, SlackAndEmailNotification.java, EmailAndSlackAndSmsNotificaton.java) that violate SRP. In addition, when we change features we have to add new classes and edit many files in your program to achieve requirements. This isn’t a good solution. Therefore, you should apply the Decorator Pattern to solve the problem and guarantee code comply SRP.

There are many definitions of the Decorator Pattern. In my opinion: Basically, the Decorator Pattern is a structural software design pattern that adds responsibilities to objects dynamically. Here is a class and sequence diagram:

Decorator Pattern. Soucre: wikipedia.org

According to the above diagram, we will modify our program to be like:

Class Diagram for Sending Notification (Email, Slack, Sms)

We will create BaseNotification.java, NotificationDecorator.java, SlackNotificationDecorator.java, SmsNotificationDecorator.java file.

Now, when we change logic sending the notification, we will only edit one line in NotificationService.java file. That is a good solution and the code complies with SRP completely.

Open/Closed Principle (OCP)

“Objects or entities should be open for extension but closed for modification.”

Open/Closed Principle (OCP)

This principle is applied widely in everywhere. For instance, you can imagine to Plugin Architecture in IntelliJ IDEA/Visual Studio Code/Eclipse: easy to add/remove a feature with an available plugin. Example: Lombok and Docker plugin in IntelliJ IDEA. I played CS(Counter-Strike) Game from high school. Do you know this game? Now, we have a new version of CS which is CS: GO (Counter-Strike: Global Offensive). The gun in this game which applied COP. Indeed, The M4A1-S (or USP-S) has a silent mode that you can remove or add it easily (right-click the mouse to unscrew the silencer).

Case study 01 (Simple)

Let me show you a simple case study: calculate the area of shapes (Rectangle, Circle, …). You can write a program with the below diagram:

Calculate areas of shapes: Rectangle, Circle.
Circle and Rectangle Class

Everything is ok !!!. Now, we have to support Triangle Shape. We have to edit the AreaCalculator.java class that violates OCP. Therefore, We need to update our program to respond to the new requirement. Consider the below class diagram:

We will create an abstract class: Shape.jave and update code of Circle.java, AreaCalculator.java, and Rectangle.java file:

Shap.java, Circle.java and Rectange.java file

With the above code, you could support many shapes: triangle, polygon, and so on without editing old code. Of course, we have to add new classes (Triangle, Pylogon, …).

Case study 02 (Use Strategy Pattern)

Problem: assume we want to build a program that displays motorbike's information: name, color, and brake behavior. In my country, the motorbike is very popular, and every day I go to my company by motorbike. We can make a program with a class diagram:

Our program will support two vehicles: AirBlade brakes without ABS(Airbalde 110/125) and Sh brakes with ABS.

Now, we have to support ToyMotobike which hasn’t brake behavior. It made of plastic or wood. Therefore, I will update the class diagram:

And I will add a new class: ToyMotobike.java.

The issue here is ToyMotobike has no brake behavior. Something is wrong !!!. And we have implemented a meaningless function. It’s a redundant function. Now, The Airblade releases a new version 150 (brake with ABS). To support this requirement we have to edit the old code that violates OCP. The solution here, we will apply the Strategy Pattern to guarantee code comply OCP.

Basically, the Strategy Pattern is a behavioral software design pattern that enables selecting an algorithm at runtime. According to the strategy pattern, the behavior of a class should not be inherited. Instead, they should be encapsulated using the interface. Please see the below diagram:

Strategy Pattern. Source: wikipedia.org

According to the above diagram, we will update our class diagram:

We will update all code of our program:

Now, our program has to support the new model: Airbalde 150 with ABS without editing old code easily. It could support a lot of different motorbike types. For instance: Wave 100, Sh mode 125, PCX 125/150, and so on. Even you wanna support ToyMotobike you only add new NoBrake.java and ToyMotobike.java class(inherit IBrakeBehavior interface) without changing code.

Liskov Substitution Principle (LSP)

“Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.”

Liskov Substitution Principle (LSP)

This principle states that objects of a parent’s class should be replaceable with objects of the child’s class without breaking the logic of the application. That requires the objects of your child’s class to behave in the same way as the objects of your parent’s class.

Case study:

Let me show you a simple case: Rectangle and Square Shape. In fact, the Square is a special case of Rectangle. So we can create Square class inherit from Rectangle class.

Square and Rectangle Class

When running our program, we will see:

*******************************
Rectangle(width: 5, height: 10) => area: 50.00
Square(width: 5, height: 5) => area: 25.00
*******************************
New Rectangle(width: 10, height: 10) => area: 100.00

Now, you can see the problem: the new rectangle object has width = 5 and height = 10 but the area = 100. It’s the wrong logic and the parent class’s object couldn’t instead of the child class’s object without altering the correctness. To solve it we will update our program by class diagram:

Here is the final code:

The output of the program:

*******************************
Rectangle(width: 5, height: 10) => area: 50.00
Square(width: 5, height: 5) => area: 25.00
*******************************
New Rectangle(width: 5, height: 10) => area: 50.00

Interface Segregation Principle (ISP)

“Many client-specific interfaces are better than one general-purpose interface.”

Interface Segregation Principle (ISP)

Like the Single Responsibility Principle(SRP), the target of the Interface Segregation Principle is to reduce the side effects and frequency of required changes by splitting the code into multiple/independent parts.

Case study: I had a daughter who is 3 years old. Her name is Min. She is very beautiful and has a lot of toys. So, I will show you a simple case: Toy for children. Toy contains properties: weight, color, … and methods: move, fly, …. So, we will create an IToy interface and all Toys will implement it.

Toy for children

We will create ToyAirPlane.java, ToyMotobike.java, and ToyHouse.java file.

ToyAirPlane.java, ToyMotobike.java, and ToyHouse.java class

After reading the above code we can see some meaningless methods. They are redundant and the client doesn’t need to implement them. So, To guarantee code abide by ISP. We will update our program by class diagram:

IFlyable.java, Imoveable.java, and IToy class
ToyAirPlane.java, ToyMotobike.java, and ToyHouse.java file

Dependency Inversion Principle (DIP)

“Entities must depend on abstractions, not on concretions. It states that the high-level module must not depend on the low-level module, but they should depend on abstractions.”

Dependency Inversion Principle (DIP)

If you are using the Spring framework to make an enterprise web application then you are using it. There are a lot of materials on the internet: tutorials, videos, books, and so on that teach/guide us to implement this principle. They teach us implementation by n-tier layer architecture: Presentation Layer (or Controller Layer), Bussiness Logic Layer(or Service Layer), Data Access Layer. Please check the simple case:

Controller Layer (high-level)

Service Layer (low-level)

MMBanerService Interface

Summary

  • Thank you for reading this article. This article includes a lot of codes, very long, and can take a long time to understand it. So, If you have any doubts/questions, please comment here.

References

--

--